Android M runtime permissions(An

作者: 5ef947bf2bd1 | 来源:发表于2018-10-28 11:23 被阅读12次

    官方介绍:
    https://developer.android.com/training/permissions/requesting?hl=zh-cn

    Google Sample地址:
    https://github.com/googlesamples/android-RuntimePermissionsBasic
    https://github.com/googlesamples/android-RuntimePermissions

    Android M引入了“运行时权限”,targetSdkVersion 23及以上的应用不但需要在AndroidManifest.xml中声明它们需要的权限,还需要在运行时动态申请这些已声明过的危险权限;targetSdkVersion低于23的应用,在安装时用户已经同意了所有权限,并且在运行时所需权限全部可用。如果不同意这些权限,用户将无法安装此应用。

    处理过程包括:

    1. 在应用的清单文件中添加使用具体功能需要的相关权限;

    2. 每次在使用需要危险权限的功能之前,检查应用是否具有相关权限(用户有可能在授权之后手动撤销权限);

    • 如果应用不具有相关权限,申请相关权限;
    • 如果应用已拥有相关权限,开始使用具体功能;
    1. 申请权限之前,帮助用户了解应用为什么需要某项权限;
    • 如果用户曾经拒绝授权,弹窗引导用户进行动态申请权限;
    • 如果用户未授权,申请相关权限;
    • 如果用户曾经拒绝授权且勾选“不再提示”选项(“不再提示”选项仅在用户已拒绝过1次授权此权限后应用再次申请用户授权此权限时显示)而导致无法显示弹窗,提示相关权限申请失败,无法使用具体功能,并告知用户开启相关权限的操作方法(设置->应用->选择应用->权限);
    1. 监听申请权限的结果,如果用户同意应用申请的权限后,开始使用具体功能;如果用户拒绝应用申请的权限,提示相关权限申请失败,无法使用具体功能。
      注:当用户授权应用同一组危险权限中的某一项权限后(如果用户不手动撤销此权限),系统将自动授权应用该组的其他权限。后期版本权限组可能会发生变化。

    场景:使用相机预览

    1. 在AndroidMinifest的根节点下声明相机权限
    <uses-permissionandroid:name="android.permission.CAMERA"/>
    
    1. 通过点击按钮,触发“开启相机预览”事件

    2. 进行权限验证

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)  != PackageManager.PERMISSION_GRANTED) {
       requestCameraPermission();
    } else {
       startCamera();// 假装有相机
    }
    
    1. 申请权限
    private void requestCameraPermission() {
        // Permission has not been granted and must be requested.
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
            //应用之前请求过此权限但用户拒绝了请求
            Snackbar.make(mLayout, R.string.camera_access_required, Snackbar.LENGTH_INDEFINITE)
                    .setAction(R.string.ok, new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                  // Request the permission
                  ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
                }
            }).show();
        } else {
            // 应用未请求过此权限或者用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了“不再提示”选项
            Snackbar.make(mLayout, R.string.camera_unavailable, Snackbar.LENGTH_SHORT).show();
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
        }
    }
    
    1. 监听申请权限结果
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_CAMERA) {
            // Request for camera permission.
            if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission has been granted. Start camera preview Activity.
                Snackbar.make(mLayout, R.string.camera_permission_granted, Snackbar.LENGTH_SHORT).show();
                startCamera();
            } else {
                // Permission request was denied.
                Snackbar.make(mLayout, R.string.camera_permission_denied, Snackbar.LENGTH_SHORT).show();
            }
        }
    }
    
    1. Fragment中验证授权和申请授权操作
    • 使用ActivityCompat.requestPermissions(getActivity(), String[], int),会执行Activity的onRequestPermissionsResult()方法;
    • 直接使用 Fragment.requestPermissions(String[], int),会执行Fragment的onRequestPermissionsResult()方法;
    1. 嵌套Fragment中申请权限操作
    • 在子Fragment中使用getParentFragment().requestPermissions()申请权限;
    • 在父Fragment中将onRequestPermissionsResutl()回调透传到子Fragmnet
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        List<Fragment> fragments = getChildFragmentManager().getFragments();
        if (fragments != null) {
            for (Fragmentfragment : fragments) {
                if (fragment != null) {
                    fragment.onRequestPermissionsResult(requestCode, permissions,grantResults);
                }
            }
        }
    }
    

    应用targetSdkVersion < 23的兼容性处理:

    场景:

    targetSdkVersion < 23的App安装在Android 6.0及以上设备时,用户已同意App中声明的所有权限,但是在安装完成后,用户手动在设置中关闭了APP的权限。此时调用ContextCompat.checkSelfPermission()检查APP是否拥有指定权限,返回值仍为PackageManager.PERMISSION_GRANTED,如果使用需要此项危险权限的功能,应用将奔溃。

    解决:

    • 方案一:升级targetSdkVersion到23或23以上版本;
    • 方案二:
      1. 获取设备Android版本,如果(Build,VERSION.SDK_INT >= Build.VERSION_CODES.M)条件成立,表示应用运行在Android 6.0及更高版本的设备上;
      2. 获取应用targetSdkVersion
    try {
        PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
        int targetSdkVersion = info.applicationInfo.targetSdkVersion;
    } catch (PackageManager.NameNotFoundExceptione) {
        e.printStackTrace();
    }
    

    a) 如果targetSdkVersion 低于23,使用PermissionChecker兼容库检查APP拥有的权限;
    b) 如果targetSdkVersion 不低于23,使用之前的方式检查APP是否拥有指定权限;

    1. 调用PermissionChecker.checkSelfPermission()检查APP是否拥有指定权限,返回值:

    a) PERMISSION_GRANTED:已授权
    b) PERMISSION_DENIED:用户拒绝授权
    c) PERMISSION_DENIED_APP_OP:用户拒绝授权且targetSdkVersion小于23

    runtime permissions处理封装库:

    鸿洋MPermissions:https://github.com/hongyangAndroid/MPermissions

    1. 引入
    • project’s build.gradle
    buildscript {
        dependencies {
            classpath'com.neenbedankt.gradle.plugins:android-apt:1.4'
        }
    }
    
    • module’s build.gradle
    apply plugin: 'com.neenbedankt.android-apt'
    
    dependencies {
        apt 'com.zhy:mpermission-compiler:1.0.0'
        compile 'com.zhy:mpermission-api:1.0.0'
    }
    
    1. 定义权限请求码
      final int REQUECT_CODE_SDCARD = 1;
    2. 判断是否需要弹出解释
    if(!MPermissions.shouldShowRequestPermissionRationale(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE, REQUECT_CODE_SDCARD)) {
        MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    }
    
    1. 申请读写SD卡权限
    MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    
    1. 处理申请权限回调
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        MPermissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
    
    @PermissionGrant(REQUECT_CODE_SDCARD)
    public voidrequestSdcardSuccess() {
        Toast.makeText(this, "GRANT ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
    }
    
    @PermissionDenied(REQUECT_CODE_SDCARD)
    public void requestSdcardFailed() {
        Toast.makeText(this, "DENY ACCESS SDCARD!",Toast.LENGTH_SHORT).show();
    }
    
    1. 混淆
    -dontwarn com.zhy.m.**
    -keep class com.zhy.m.** {*;}
    -keep interface com.zhy.m.** { *; }
    -keep class **$$PermissionProxy { *; }
    

    PermissionsDispatcher:https://github.com/hotchemi/PermissionsDispatcher

    1. 引入:
    • module’s build.gradle
    dependencies {
       implementation 'com.github.hotchemi:permissionsdispatcher:${latest.version}'
       annotationProcessor 'com.github,hotchemi:permissionsdispatcher-processor:${latest.version}'
    }
    
    • 安装PermissionsDispatcher插件
      File->Settings->Plugins,搜索PermissionsDispatcher,点击install安装,重启AS。


      image.png
    • 生成申请权限操作代码
      ⅰ. Code->Generate->GenerateRuntime Permissions


      image.png

      ⅱ. 勾选要申请的权限以及要覆盖的方法,点击Generate生成代码

    @NeedsPermission 用户同意授权应用相关权限后的回调;
    @OnShowRationale 用户已拒绝过1次授权此权限后,应用再次请求用户授权此权限时的回调。在此方法中解释应用为什么需要此权限。之后调用PermissionRequest的proceed()方法继续申请权限;
    @OnPermissionDenied 用户拒绝授权应用相关权限后的回调;
    @OnNeverAskAgain 当用户拒绝授权并勾选“不再提示”后的回调

    • 申请权限
    xxActivityPermissionsDispatcher.**WithPermissionCheck(this);//  xx为自动生成的辅助类,**为被@NeedsPermission注解的方法名
    

    常规权限列表:

    ACCESS_LOCATION_EXTRA_COMMANDS
    ACCESS_NETWORK_STATE
    ACCESS_NOTIFICATION_POLICY
    ACCESS_WIFI_STATE
    BLUETOOTH
    BLUETOOTH_ADMIN
    BROADCAST_STICKY
    CHANGE_NETWORK_STATE
    CHANGE_WIFI_MULTICAST_STATE
    CHANGE_WIFI_STATE
    DISABLE_KEYGUARD
    EXPAND_STATUS_BAR
    GET_PACKAGE_SIZE
    INSTALL_SHORTCUT
    INTERNET
    KILL_BACKGROUND_PROCESSES
    MODIFY_AUDIO_SETTINGS
    NFC
    READ_SYNC_SETTINGS
    READ_SYNC_STATS
    RECEIVE_BOOT_COMPLETED
    REORDER_TASKS
    REQUEST_INSTALL_PACKAGES
    SET_ALARM
    SET_TIME_ZONE
    SET_WALLPAPER
    SET_WALLPAPER_HINTS
    TRANSMIT_IR
    UNINSTALL_SHORTCUT
    USE_FINGERPRINT
    VIBRATE
    WAKE_LOCK
    WRITE_SYNC_SETTINGS

    危险权限列表:

    group:android.permission-group.CONTACTS

    permission:android.permission.WRITE_CONTACTS
    permission:android.permission.GET_ACCOUNTS
    permission:android.permission.READ_CONTACTS

    group:android.permission-group.PHONE

    permission:android.permission.READ_CALL_LOG
    permission:android.permission.READ_PHONE_STATE
    permission:android.permission.CALL_PHONE
    permission:android.permission.WRITE_CALL_LOG
    permission:android.permission.USE_SIP
    permission:android.permission.PROCESS_OUTGOING_CALLS
    permission:com.android.voicemail.permission.ADD_VOICEMAIL

    group:android.permission-group.CALENDAR

    permission:android.permission.READ_CALENDAR
    permission:android.permission.WRITE_CALENDAR

    group:android.permission-group.CAMERA

    permission:android.permission.CAMERA

    group:android.permission-group.SENSORS

    permission:android.permission.BODY_SENSORS

    group:android.permission-group.LOCATION

    permission:android.permission.ACCESS_FINE_LOCATION
    permission:android.permission.ACCESS_COARSE_LOCATION

    group:android.permission-group.STORAGE

    permission:android.permission.READ_EXTERNAL_STORAGE
    permission:android.permission.WRITE_EXTERNAL_STORAGE

    group:android.permission-group.MICROPHONE

    permission:android.permission.RECORD_AUDIO

    group:android.permission-group.SMS

    permission:android.permission.READ_SMS
    permission:android.permission.RECEIVE_WAP_PUSH
    permission:android.permission.RECEIVE_MMS
    permission:android.permission.RECEIVE_SMS
    permission:android.permission.SEND_SMS
    permission:android.permission.READ_CELL_BROADCASTS

    导入Google Sample—RuntimePermissions时,build失败解决方案:

    • 原因:settings.gradle中引入的模块为Application,但是Application缺少build.gradle。
    • 解决:File->Open->RuntimePermissions项目->kotlinApp,build后(AS可能需要下载kotlin插件,允许后等待下载完成)可以正常运行项目。

    这里是《我的Android世界》系列目录

    相关文章

      网友评论

        本文标题:Android M runtime permissions(An

        本文链接:https://www.haomeiwen.com/subject/xljptqtx.html