UriHandler
とは
UriHandler
は androidx.compose.ui.platform
が提供する、URI(Web ページ、ファイル、外部アプリなど)を開くためのインターフェースです。Composable 関数からは、以下のようにシンプルに呼び出せます。
@Composable
fun OpenUriButton(uri: String) {
val uriHandler = LocalUriHandler.current
Button(onClick = { uriHandler.openUri(uri) }) {
Text(text = "Open URI")
}
}
Android におけるデフォルトの UriHandler
LocalUriHandler.current
で取得されるのは、各プラットフォームに応じた UriHandler
の実装です。Android 環境では、デフォルトとして AndroidUriHandler
が使用されます。
AndroidUriHandler
では、URI に対応するアプリがインストールされていない場合や、URI の形式が無効な場合などに IllegalArgumentException
が throw されます。そのため、呼び出し側ではクラッシュ(アプリの異常終了)を防ぐため、例外処理を行う必要があります。
AndroidUriHandler.android.kt 全文
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.ui.platform
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
class AndroidUriHandler(private val context: Context) : UriHandler {
/**
* Open given URL in browser
*
* @throws IllegalArgumentException when given [uri] is invalid and/or can't be handled by the
* system
*/
override fun openUri(uri: String) {
try {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(uri)))
} catch (e: ActivityNotFoundException) {
throw IllegalArgumentException("Can't open $uri.", e)
}
}
}
class AndroidUriHandler(private val context: Context) : UriHandler {
override fun openUri(uri: String) {
try {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(uri)))
} catch (e: ActivityNotFoundException) {
throw IllegalArgumentException("Can't open $uri.", e)
}
}
}
簡単に安全に URI を開くために
記事中のリンクやお知らせなど、ユーザーに URI を開かせたいシーンは多々あります。ほとんどの場合では URI を開くのに失敗したときは、ユーザーにそのことを伝えれば十分です。
しかし、毎回呼び出し側で例外処理を実装するのは煩雑です。また、ライブラリや SDK 内で UriHandler
を呼び出している場合は、例外処理を差し込むのが難しいケースもあります。
そこで、例外を throw する代わりに Toast を表示する独自の UriHandler
(SafeUriHandler
)を実装しました1。CompositionLocalProvider
を使用して LocalUriHandler
を上書きすることで、以降の処理では LocalUriHandler.current
を通じて SafeUriHandler
を取得できます。これにより、アプリケーション全体で安全な URI オープン処理を適用することが可能です。
class SafeUriHandler(private val context: Context) : UriHandler {
override fun openUri(uri: String) {
try {
context.startActivity(Intent(Intent.ACTION_VIEW, uri.toUri()))
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, "Can't open $uri.", Toast.LENGTH_SHORT).show()
}
}
}
@Composable
fun MyApp() {
val context = LocalContext.current
val uriHandler = remember(context) { SafeUriHandler(context) }
CompositionLocalProvider(
LocalUriHandler provides uriHandler,
) {
// ...
}
}
URI を開ける場合 | URI を開けない場合 |
---|---|
![]() |
![]() |
まとめ
-
LocalUriHandler.current
を使用すると、プラットフォーム標準のUriHandler
を取得できます - デフォルトの
AndroidUriHandler
は、URI の起動に失敗した際に例外を throw し、アプリがクラッシュする可能性があります -
SafeUriHandler
のように例外を throw しない独自のUriHandler
を実装することで、呼び出し側での例外処理が不要になります -
CompositionLocalProvider
を使ってLocalUriHandler
を上書きすることで、アプリ全体を通して独自処理利用できます
参考文献
-
SafeUriHandler
は簡易な通知手段として Toast を利用していますが、UI の状態に応じて Snackbar や Dialog を用いる方が適切な場合もあります。 ↩