記事ディレクトリ
序文
- Android が危険なパーミッションを動的に適用する必要がある理由を学ぶ
- Android アプリで危険な許可を申請する方法を学ぶ
参考
- Android 公式ドキュメント: アプリケーションのアクセス許可のリクエスト
- アプリケーションがアクセス許可を申請する必要があるかどうかを評価します。システムは、カメラのアクセス許可を申請せずに写真を撮ったりビデオを録画したりするなど、いくつかの代替ソリューションを提供します。
1. アプリケーションのアクセス許可を要求する基本原則
1. ユーザーが関連する権限を必要とする機能を操作し始めたら、特定の使用シナリオで権限を要求します。
解释:在权限需要被使用的功能去申请。早期的应用基本都是把所有的权限放在应用启动去申请,用户不同意所有权限直接阻断了用户使用软件的权利,这是不可取的,因为一个应用中有很多功能是不需要某个特定权限就能够被使用的。
2. ユーザーがアプリケーションを使用するのを妨げないでください。パーミッションが要求された理由を説明するフローなど、UI フローのガイドをキャンセルするオプションをユーザーに常に提供します。
解释:还是第一条解释的原因,不要因为用户不同意某一个单一功能的权限而阻止用户使用其他功能。
3. ユーザーが特定の機能に必要な許可を拒否または取り消した場合は、ユーザーが引き続きアプリを使用できるように、アプリを適切にダウングレードしてください (許可が必要な機能を無効にすることが考えられます)。
解释:用户如果拒绝了某个功能需要的权限,那就提醒用户此功能需要打开需要的权限才能被使用,用户不同意权限就不能使用当前功能。
4. システムの動作について推測しないでください。たとえば、特定の権限が同じ権限グループに表示されるとします。権限グループの目的は、アプリが密接に関連する複数の権限を要求するときに、システムがユーザーに表示されるシステム ダイアログの数を最小限に抑えることです。
解释:明确指定需要使用的特殊权限,比如读写sdcard权限,如果读写权限都需要,那就都需要动态申请。
2. 許可申請の流れ(公式サイトより抜粋)
-
アプリのマニフェスト ファイルで、アプリが要求する権限を宣言します。
アプリが要求する権限を宣言するには、適切な要素をアプリのマニフェスト ファイルに追加します。たとえば、アプリケーションがカメラにアクセスする必要がある場合は、次の行を AndroidManifest.xml に追加する必要があります。
<manifest ...>
<uses-permission android:name="android.permission.CAMERA"/>
<application ...>
...
</application>
</manifest>
-
アプリの特定のアクションが特定のランタイム権限に関連付けられるように、アプリのユーザー エクスペリエンスを設計します。個人データにアクセスするためのアクセス許可をアプリに付与する必要がある可能性があるアクションについて、ユーザーに通知します。
设计UX告知用户我们可能需要申请的权限,以及为什么需要使用这些权限,应用在哪些功能
-
特定のユーザーのプライベート データへのアクセスを必要とするアプリで、ユーザーがタスクまたはアクションを呼び出すのを待ちます。その時点で、アプリは、データへのアクセスに必要なランタイム アクセス許可を要求できます。
就是当用户调用特定的功能的时候再去申请权限,不要提前申请权限,因为有些需要权限的功能可能用户都不会用到,但是我们不能阻止用户使用其他功能
-
ユーザーがアプリに必要なランタイム権限を付与したことを確認します。承認されている場合、アプリはプライベート ユーザー データにアクセスできます。そうでない場合は、次のステップに進みます。その権限を必要とする操作を実行するたびに、その権限があることを確認する必要があります。
通过Android的ContextCompat.checkSelfPermission()方法检测用户是否已经授权,每次执行都需要判断权限是否授予,因为用户可用通过应用信息主动关闭权限
-
アプリが特定の実行時パーミッションをユーザーに付与する必要がある理由をアプリがユーザーに表示する必要があるかどうかを確認します。アプリに理由を表示すべきではないとシステムが判断した場合は、UI 要素を表示せずに次のステップに直接進みます。
ただし、アプリが理由を表示する必要があるとシステムが判断した場合は、アプリがアクセスしようとしているデータと、ランタイム許可が付与された場合にアプリがユーザーに提供する利点を明確に示す UI 要素でユーザーに理由を表示します。 . ユーザーが理由を確認したら、次のステップに進みます。
就是当用户执行了拒绝之后,就会走到当前步骤,我们需要给用户展示为什么需要使用这个权限,并且引导用户去权限设置中打开当前权限
-
アプリがユーザーのプライベート データにアクセスするために必要な実行時のアクセス許可を要求します。パーミッションの概要ページに表示されるプロンプトなど、実行時のパーミッション プロンプトが表示されます。
调用申请权限的方法,系统弹出权限申请弹窗
7. ユーザーの応答を確認します。ユーザーは、実行時のアクセス許可に同意するか拒否するかを選択できます。
用户可以在弹窗选择同意或者拒绝,或者只是同意当前一次权限
-
ユーザーがアプリにアクセス許可を付与すると、ユーザーのプライベート データにアクセスできます。ユーザーが許可の付与を拒否した場合は、アプリのエクスペリエンスを適切に低下させて、アプリが許可によって保護されている情報を取得することなくユーザーに機能を提供できるようにしてください。
当用户选择不同意当前权限时候,我们不提供当前功能给用户使用
公式 Web サイトからコピーします。Android で実行時のアクセス許可を宣言および要求するためのワークフローです。
3. 許可コードのリクエスト
1.システム管理許可リクエストコードを許可する
モジュールの build.gradle ファイルに次のライブラリの依存関係を追加します。
- androidx.activity、バージョン 1.2.0 以降
- androidx.fragment、バージョン 1.3.0 以降
// Activity引入
dependencies {
val activity_version = "1.6.1"
// Java language implementation
implementation("androidx.activity:activity:$activity_version")
// Kotlin
implementation("androidx.activity:activity-ktx:$activity_version")
}
// Fragment引入
dependencies {
val fragment_version = "1.5.4"
// Java language implementation
implementation("androidx.fragment:fragment:$fragment_version")
// Kotlin
implementation("androidx.fragment:fragment-ktx:$fragment_version")
// Testing Fragments in Isolation
debugImplementation("androidx.fragment:fragment-testing:$fragment_version")
}
次の 2 つのコントラクト クラスを使用して、アクセス許可を申請します。
- アクセス許可を要求するには、RequestPermission を使用します。
- 同時に複数のアクセス許可を要求するには、RequestMultiplePermissions を使用します。
この例では、RequestMultiplePermissions を使用してリクエストを作成します. RequestPermission も同様です. 単一の許可を申請する例は、公式 Web サイトで入手できます. 公式 Web サイトで例を表示できます.
-
アクティビティまたはフラグメントの初期化ロジックで、ActivityResultCallback の実装を registerForActivityResult() の呼び出しに渡します。ActivityResultCallback は、権限要求に対するユーザーの応答をアプリが処理する方法を定義します。
保留对 registerForActivityResult()(类型为 ActivityResultLauncher)的返回值的引用。
-
必要に応じてシステム権限ダイアログを表示するには、前の手順で保存した ActivityResultLauncher インスタンスで launch() メソッドを呼び出します。
launch() を呼び出した後、システム権限ダイアログが表示されます。ユーザーが選択を行うと、システムは前の手順で定義した ActivityResultCallback 実装を非同期的に呼び出します。
companion object {
private val PERMISSION_ARR = arrayOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
)
}
private fun handlePermission() {
PERMISSION_ARR.forEach {
permission ->
when {
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -> {
Log.d(TAG, "权限允许:: $permission")
}
shouldShowRequestPermissionRationale(permission) -> {
// 在此处给用户解释为甚么需要申请此权限
Log.d(TAG, "权限拒绝:: $permission")
}
else -> {
requestPermissionLauncher.launch(PERMISSION_ARR)
}
}
}
}
private val requestPermissionLauncher: ActivityResultLauncher<Array<String>> =
registerForActivityResult(RequestMultiplePermissions()) {
map ->
map?.keys?.forEach {
key ->
// key是权限,value是当前权限的是否被允许的boolean值
// 申请权限之后的操作就在此执行
Log.d(TAG, "permission key:: $key, isGranted:: ${
map[key]}")
}
}
2. 自主管理許可申請コード
companion object {
private const val TAG = "MainActivity"
private val PERMISSION_ARR = arrayOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
)
private const val PERMISSION_REQUEST_CODE = 100
}
private fun handlePermission() {
PERMISSION_ARR.forEach {
permission ->
when {
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -> {
Log.d(TAG, "权限允许:: $permission")
}
shouldShowRequestPermissionRationale(permission) -> {
Log.d(TAG, "权限拒绝:: $permission")
}
else -> {
// 请求权限
requestPermissions(arrayOf(permission), PERMISSION_REQUEST_CODE)
}
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
PERMISSION_REQUEST_CODE -> {
if ((grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
// 权限允许
} else {
// 权限不允许,不能允许用户使用
}
return
}
else -> {
// Ignore all other requests.
}
}
}
要約する
自身の使い方を簡単にまとめたもので、基本的にはすべて公式サイトで入手可能です。
建议使用第一种方式,因为在Fragment和Activity中都可以玩