Android

Android 6.0 动态权限Permission相关

2017-07-10  本文已影响0人  大荣言午

Andriod 6.0 动态权限Permission相关

推荐博文:

随着Android 6.0发布以及普及,我们开发者所要应对的主要就是新版本SDK带来的一些变化,首先关注的就是权限机制的变化。对于6.0的几个主要的变化,查看查看官网的这篇文章http://developer.android.com/intl/zh-cn/about/versions/marshmallow/android-6.0-changes.html,其中当然包含Runtime Permissions

相关知识点

新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。

targetSdkVersion和minSdkVersion相关的区别

关于taretSdkVersion和minSdkVersion的区分相信很多人都不是太清楚,在这里推荐Android Min SDK Version vs. Target SDK Version,对相关的设置有点说明。这两者相当于一个区间,你可以用到targetSDK中最新的API和最酷的新功能,但你又不得不向下兼容到minSDK,保证这个区间内的设备都可以正常的运行你的app。换句话说,你想使用Android刚刚推出的新特性,但这对于你的app又不是必须的,你就可以将targetSDK设置为你想使用新特性的SDK版本,minSDK设置成低版本保证所有人都可以使用你的app。

参考博文链接:http://www.jianshu.com/p/a37f4827079a

下面来段代码示例(为了向下兼容,这里我采用了v4包下的ContextCompat和ActivityCompat):

 View.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //判断当前系统是否高于或等于6.0
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                //当前系统大于等于6.0
                if (ContextCompat.checkSelfPermission(MineInforActivity.this,Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                    //具有拍照权限,直接调用相机
                    //具体调用代码
                } else {
                    //不具有拍照权限,需要进行权限申请
                    ActivityCompat.requestPermissions(MineInforActivity.this,new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION_CAMERA_CODE);
                }
            } else {
                //当前系统小于6.0,直接调用拍照

            }
        }
    });

如果用户勾选了不再提醒,然后把你拒绝了,那你的应用就GG了,其实这里还有一个API方法:

    if(!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)){
                        //如果用户勾选了不再提醒,则返回false
                        //给予用户提醒,比如Toast或者对话框,让用户去系统设置-应用管理里把相关权限打开    
                    }

ContextCompat.checkSelfPermission无效的问题

在做项目中发现,我在使用ContextCompat.checkSelfPermission时,无论如何开关权限返回值都是PackageManager.PERMISSION_GRANTED,而使用PackageManager.checkPermission()的时候返回值又始终都是PackageManager.PERMISSION_DENIED;
经过查询相关文档及博客发现:

**If your application is targeting an API level before 23 (Android M) then both:ContextCompat.CheckSelfPermission and Context.checkSelfPermission doesn't work and always returns 0 (PERMISSION_GRANTED). Even if you run the application on Android 6.0 (API 23).
**
在targetSdkVersion小于23(Android M)的时候,ContextCompat.CheckSelfPermission 和Context.checkSelfPermission方法都不能正常工作并且始终返0(PERMISSION_GRANTED),即使你的应用运行在Android6.0(API 23)的设备上。

解决办法:

As I said in the 1st point, if you targeting an API level before 23 on Android 6.0 then ContextCompat.CheckSelfPermission and Context.checkSelfPermission doesn't work. Fortunately you can use PermissionChecker.checkSelfPermission to check run-time permissions.
使用permissionChecker.checkSelfPermission,来检测权限是否被授予。

关于权限android.permission.READ_PHONE_STATE和 WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE问题

参考博文
以上两个权限对应用运行时影响最大,其中READ_PHONE_STATE用来获取deviceID,即IMEI号码。这是很多统计依赖计算设备唯一ID的参考。如果新的权限导致读取不到,避免导致统计的异常。建议在完全支持运行时权限之前,将对应的值写入到App本地数据中,对于新安装的,可以采取其他策略减少对统计的影响。

WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE这两个权限和外置存储(即sdcard)有关,对于下载相关的应用这一点还是比较重要的,我们应该尽可能的说明和引导用户授予该权限。

关于权限android.permission.READ_PHONE_STATE,系统会弹出一个对话框提醒撤销的危害,如果用户执意撤销,会带来如下的反应
如果你的程序正在运行,则会被杀掉。
当你的应用再次运行时,可能出现崩溃
为什么会可能崩溃的,比如下面这段代码

    TelephonyManager telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
    String deviceId = telephonyManager.getDeviceId();
    if (deviceId.equals(mLastDeviceId)) {//This may cause NPE
      //do something
    }

如果用户撤消了获取DeviceId的权限,那么再次运行时,deviceId就是null,如果程序后续处理不当,就会出现崩溃。
目前我在项目中做了版本控制,当版本较低是还是用老的方法,做了向下兼容。

service与动态权限管理兼容问题

requestPermission()can only be called from an Activity and not a Service (unlike checkPermission() that only requires PackageManager). So you need to do some extra work to get around that; you do need to provide an Activity in your app and, for example, your Service can check for permissions it needs and if they have not been granted yet, it can create a notification and that can inform user with a descriptive short message as to why there is a notification and what needs to happen when they click on the notification, etc.

requestPermission()需要用户提供Activity,在service里使用存在问题,可以在service里面先执行checkPermission方法,判断是否授予权限,鄙人技术有限没能解决这个问题,不过有技术大牛实现相关问题GitHub传送门有兴趣的可以下载先来研究研究源码,这里我先做下相关备注以便后面学习。

部分手机兼容存在禁止权限却始终返回PERMISSION_GRANTED

这种情况部分手机解决方案跟上述ContextCompat.checkSelfPermission无效问题类似,先设置targetSdkVersion>=23,再设置ContextCompat.checkSelfPermission()改为permissionChecker.checkSelfPermission()方法,来检测是否授予权限。
但是针对魅族、小米手机,还是存在无效问题,这就需要对应的处理,还去大佬指教……

BaseActivity部分核心代码

1、先检测权限是否授予

 * 检测所有的权限是否都已授权
 * 
 * @param permissions
 * @return
 */
private boolean checkPermissions(String[] permissions) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        return true;
    }
    for (String permission : permissions) {
        if (PermissionChecker.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;

}

2、获取权限集合中权限列表

 * 获取权限集中需要申请权限的列表
 * 
 * @param permissions
 * @return
 */
private List<String> getDeniedPermissions(String[] permissions) {
    List<String> needRequestPermissionList = new ArrayList<>();
    for (String permission : permissions) {
        if (PermissionChecker.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED
                || ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
            needRequestPermissionList.add(permission);
        }
    }
    return needRequestPermissionList;
}

3、请求所需权限

 * 请求权限
 * 
 * @param permissions
 *            请求的权限
 * @param requestCode
 *            请求权限的请求码
 */
public void requestPermission(String[] permissions, int requestCode) {
    this.REQUEST_CODE_PERMISSION = requestCode;
    if (checkPermissions(permissions)) {
        permissionSuccess(REQUEST_CODE_PERMISSION);
    } else {
        List<String> needPermissions = getDeniedPermissions(permissions);
        ActivityCompat.requestPermissions(this, needPermissions.toArray(new String[needPermissions.size()]),
                REQUEST_CODE_PERMISSION);
    }
}

4、确定所有权限是否都已授权

 * 确认所有的权限是否都已授权
 * 
 * @param grantResults
 * @return
 */
private boolean verifyPermissions(int[] grantResults) {
    for (int grantResult : grantResults) {
        if (grantResult != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;
}

5、系统请求权限回调执行对应的操作

 * 系统请求权限回调
 * 
 * @param requestCode
 * @param permissions
 * @param grantResults
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
        @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CODE_PERMISSION) {
        if (verifyPermissions(grantResults)) {
            permissionSuccess(REQUEST_CODE_PERMISSION);
        } else {
            permissionFail(REQUEST_CODE_PERMISSION);
        }
    }
}

6、子类继承实现方法执行相应的操作

 * 获取权限成功
 * 
 * @param requestCode
 */
public void permissionSuccess(int requestCode) {
    Log.e("TAG", "获取权限成功=" + requestCode);

}

/**
 * 权限获取失败
 * 
 * @param requestCode
 */
public void permissionFail(int requestCode) {
    Log.e("TAG", "获取权限失败=" + requestCode);
}

欢迎各位大佬给予宝贵意见……

上一篇下一篇

猜你喜欢

热点阅读