从0到1打造一款安卓app之16-Activity Results API与权限处理

从0到1打造一款安卓app之16-Activity Results API与权限处理

随着安卓的不断升级,在6.0时出现了动态权限的概念。

1.1AndroidManifest.xml中声明权限

<manifest ...>
    <!-- 访问相机 -->
    <uses-permission android:name="android.permission.CAMERA"/>
    <application ...>
        ...
    </application>
</manifest>

如果App中用到了硬件,如相机,建议加上可选声明,不加的话,Android系统会认为你的App要在有该硬件的情况下才能运行,如果没此硬件的话直接就阻止你App的安装。但大多数情况下,我们的App在没有该硬件的设备上也能运行,所以建议还是加上:

<manifest ...>
    <application>
        ...
    </application>
    <uses-feature android:name="android.hardware.camera"
                  android:required="false" />
<manifest>

用到此硬件时再执行下判断,有就执行正常逻辑,没有就执行其他逻辑:

判断是否有摄像头

cameraManager = requireContext().getSystemService(Context.CAMERA_SERVICE) as CameraManager
packageManager = requireContext().packageManager

if (!packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
    return
}

1.以前申请权限

1.2申请权限

• 调用ContextCompat.checkSelfPermission()判断是否具有相应权限,此方法会返回:PERMISSION_GRANTED(已授权) 或PERMISSION_DENIED(未授权);

• 未授权的话调用ActivityCompat.requestPermissions()申请权限;

• 重写onRequestPermissionsResult()回调方法,对授权结果进行判定,执行后续操作。

onRequestPermissionsResult方法在ActivityCompat里有定义,

Activity里与权限相关的方法还有

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
        @NonNull int[] grantResults) {
    /* callback - no nothing */
}
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {}
public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
    return getPackageManager().shouldShowRequestPermissionRationale(permission);
}

上面三个方法,在Fragment里也有相应的定义,不过Fragment里的requestPermissions已经被标记为Deprecated

@Deprecated
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {}

并推荐使且registerForActivityResult代替

}@MainThread
@NonNull
@Override
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
        @NonNull final ActivityResultContract<I, O> contract,
        @NonNull final ActivityResultCallback<O> callback) {}

2.Activity Results API申请权限

新的动态权限申请,主要的api为registerForActivityResult这一个方法,它是接口ActivityResultCaller 里定义的方法,ComponentActivityFragment里都继承了ActivityResultCaller

@NonNull
@Override
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
        @NonNull final ActivityResultContract<I, O> contract,
        @NonNull final ActivityResultRegistry registry,
        @NonNull final ActivityResultCallback<O> callback) {
    return registry.register(
            "activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
}

涉及到的相关的类有

ActivityResultContract
ActivityResultLauncher
ActivityResultCallback
ActivityResultRegistry

ActivityResultContract的子类有如下几种,也就是说Activity Results API除了可以用来申请权限,还可以做其他事情

CaptureVideo in ActivityResultContracts (androidx.activity.result.contract)
CreateDocument in ActivityResultContracts (androidx.activity.result.contract)
GetContent in ActivityResultContracts (androidx.activity.result.contract)
GetMultipleContents in ActivityResultContracts (androidx.activity.result.contract)
OpenDocument in ActivityResultContracts (androidx.activity.result.contract)
OpenDocumentTree in ActivityResultContracts (androidx.activity.result.contract)
OpenMultipleDocuments in ActivityResultContracts (androidx.activity.result.contract)
PickContact in ActivityResultContracts (androidx.activity.result.contract)
RequestMultiplePermissions in ActivityResultContracts (androidx.activity.result.contract)
RequestPermission in ActivityResultContracts (androidx.activity.result.contract)
StartActivityForResult in ActivityResultContracts (androidx.activity.result.contract)
StartIntentSenderForResult in ActivityResultContracts (androidx.activity.result.contract)
TakePicture in ActivityResultContracts (androidx.activity.result.contract)
TakePicturePreview in ActivityResultContracts (androidx.activity.result.contract)
TakeVideo in ActivityResultContracts (androidx.activity.result.contract)

2.1申请单个权限

val activityResultLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
    LogUtils.d(if (result) "权限申请成功" else "权限被拒绝")
    if (result) {
        return@registerForActivityResult
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val shouldShowRationale = shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)
        LogUtils.d("shouldShowRationale:$shouldShowRationale")
        if (shouldShowRationale) {
            //权限被拒绝(没有选择不再提醒,还有机会重新申请)
            ToastUtils.showShort("申请时,被用户拒绝了")
        } else {
            ToastUtils.showLong("申请时或者之前就已经被用户拒绝了,并且选择了不再提醒,只能跳转到设置页手动赋予应用权限")
        }
    }
}
activityResultLauncher.launch(Manifest.permission.CAMERA)

shouldShowRequestPermissionRationale,“应不应该解释下请求这个权限的目的”。

禁止有两种情况,并不是所有禁止shouldShowRequestPermissionRationale都会返回true

1.都没有请求过这个权限,用户不一定会拒绝你,所以你不用解释,故返回false; 2.请求了但是被拒绝了,此时返回true,意思是你该向用户好好解释下了; 3.请求权限被禁止了,也不给你弹窗提醒了,所以你也不用解释了,故返回fasle; 4.请求被允许了,都给你权限了,还解释个啥,故返回false。

2.2申请多个权限

与申请单个权限一样,

只是把ActivityResultContracts.RequestPermission()换成ActivityResultContracts.RequestMultiplePermissions()

activityResultLauncher.launch(String)变成activityResultLauncher.launch(arrayOf())

返回结果也不是一个Boolean而是Map<String,Boolean>

val activityResultLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result ->
    //具体情况具体处理
    result.forEach{
        LogUtils.d("权限:${it.key}:${it.value}")
    }
}
activityResultLauncher.launch(
    arrayOf(
        Manifest.permission.CAMERA,
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
    )
)

3.权限常识

Android 中的权限

Manifest.permission

权限分类

1) 安装时权限

系统会在用户允许安装该应用时自动授予相应权限(需在应用中声明),分为两个子类型:

普通权限 (normal)

不会威胁到用户安全和隐私的权限,只需在AndroidManifest.xml中声明下就能直接使用。

签名权限 (signature)

当应用声明了其他应用已定义的签名权限时,如果两个应用使用同一个签名文件进行签名,系统会在安装时向前者授予该权限。否则,系统无法向前者授予该权限。

2) 运行时权限 (dangerous)

Android 6.0 (M) 的新特性,又称危险权限,指的是可能会触及用户隐私或对设备安全性造成影响的权限,如获得联系人信息、访问位置等。此类权限需要在代码进行申请,系统弹出许可对话框,当用户手动同意后才会获得授权。

3) 特殊权限

比较少见,Google认为此类权限比危险权限更敏感,因此需要让用户到专门的设置页面手动对某个应用程序授权。如:悬浮框权限、修改设置权限、管理外部存储等。特殊权限需要特殊处理!!!

Tips:(两个权限相关的adb命令)

# 查看应用拥有的权限
adb shell dumpsys package 应用包名

# 获取权限等级
adb shell dumpsys package permision |grep -i prot

参考资料

新技术实战,一步步用Activity Results API封装权限申请库

猜你喜欢

转载自juejin.im/post/7112458953530818573