小序
原本以为这次项目完成得不错,结果测试妹子在禅道上又提了些BUG,大多涉及到6.0权限的问题。这就比较尴尬了,毕竟是一开始并没有顾虑的。而关于权限申请,其实际上已经有很多前辈提供过相应的解决办法,在此谢过。
正文
1. 需动态申请的权限
权限的申请依据谷歌的分类来讲,一类属于普通的权限申请,与往常一样可直接在 AndroidManifest.xml 注册就可以了;另一类则需要在应用程序使用的地方执行动态申请,该类权限属危险权限(组),当然前提也得先在 AndroidManifest.xml 下注册声明。这里仅罗列需要动态申请的权限内容(摘自这里)。
权限组 | 权限 |
---|---|
CALENDAR 日历 | android.permission.READ_CALENDAR android.permission.WRITE_CALENDAR |
CAMERA 相机 | android.permission.CAMERA |
CONTACTS 联系人 | android.permission.READ_CONTACTS android.permission.WRITE_CONTACTS android.permission.GET_ACCOUNTS |
LOCATION 位置 | android.permission.ACCESS_FINE_LOCATION android.permission.ACCESS_COARSE_LOCATION |
MICROPHONE 麦克风 | android.permission.READ_PHONE_STATE android.permission.CALL_PHONE android.permission.READ_CALL_LOG android.permission.WRITE_CALL_LOG com.android.voicemail.permission.ADD_VOICEMAIL android.permission.USE_SIP android.permission.PROCESS_OUTGOING_CALLS |
PHONE 电话 | android.permission.READ_PHONE_STATE android.permission.CALL_PHONE android.permission.READ_CALL_LOG android.permission.WRITE_CALL_LOG com.android.voicemail.permission.ADD_VOICEMAIL android.permission.USE_SIP android.permission.PROCESS_OUTGOING_CALLS |
SENSORS 传感器 | android.permission.BODY_SENSORS |
SMS 短信 | android.permission.SEND_SMS android.permission.RECEIVE_SMS android.permission.READ_SMS android.permission.RECEIVE_WAP_PUSH android.permission.RECEIVE_MMS android.permission.READ_CELL_BROADCASTS |
STORAGE 存储 | android.permission.READ_EXTERNAL_STORAGE android.permission.WRITE_EXTERNAL_STORAGE |
2. 具体实现
了解完需要申请的权限之后,需要根据实际项目当中需要用到的相关权限,在相应的位置开始我们的权限申请。申请过程主要包括以下两个操作,另外也有人封装了些简单易用的依赖库,有兴趣的亲也可了解下。
/**
* 以定动态申请位权限为例
*/
public void handPermission() {
// 定位权限组
String[] mPermissionGroup = new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION};
// 过滤已持有的权限
List<String> mRequestList = new ArrayList<>();
for (String permission : mPermissionGroup) {
if ((ContextCompat.checkSelfPermission(getActivity(), permission)
!= PackageManager.PERMISSION_GRANTED)) {
mRequestList.add(permission);
}
}
// 申请未持有的权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !mRequestList.isEmpty()) {
ActivityCompat.requestPermissions(mActivity, mRequestList.toArray(
new String[mRequestList.size()]), 100);
} else {
// 权限都有了,就可以继续后面的操作
}
}
/**
* 回调权限申请结果
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 100: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 同意权限申请,就可以继续后面的操作
} else {
// 拒绝权限申请,提示申请权限的重要性
}
}
break;
}
}
3. 巧遇问题
a. 反复申请
“重复申请”是不合理的,这样会严重影响到用户的体验。最好是在用户拒绝权限的申请用给予用户用好提示,或本次应用开启权限仅申请一次,不通过提示为授权之类的。
部分机子系统需要特别注意,因为它们整个应用的申请仅提示一次,如最为熟悉的小米/红米。无论之后再怎么requestPermissions也无济于事,到头来还得提示用户到应用设置手动启动权限。这算是比较恶心的一个事情,且其他机子上如果用户勾选了不再提示也是一样的。
建议权限申请不要再onResume()的生命周期中执行,因为权限申请操作后会再次调用onResume(),个人在小米/红米机子上测试出现过该问题。
b. 多权限申请带来的问题
String[] mPermissionGroup = new String[]{
Manifest.permission.CAMERA
Manifest.permission.ACCESS_COARSE_LOCATION};
// 注意:这样的多权限申请是存在问题的
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityCompat.requestPermissions(mActivity, mPermissionGroup, 100);
}
如上面所见,我定义了一个需要权限请求的数组。里面包括了两个权限组,一个是照相机的权限,另一个则是定位的权限(权限组当中的任意成员被授予权限,相当于权限组内的各个权限都将被授予权限)。那么在实际执行当中会是什么效果呢?
经上面图示效果可以看到,多权限申请的时候每一个权限组的申请都被单独开来了。那么问题来了,因为权限的申请是我们主动发出的,并且一个应用当中往往不止一个地方需要权限申请。简单的说如果你已经执行百度定位授予了定位权限了。那么你下次就应该先判断定位权限是否已被授权再加入到多权限申请单中。
那么有人就要问了,即使一开始不判断是否授权。在申请授权的回调过程中不也能自己判断麽?嗯,确实是这样没错。但那只是针对mPermissionGroup这个整体,即如果当中有部分权限已经被授予,而剩下的未授予,将全部被重新申请。
E/SQLiteDatabase: Failed to open database ‘/storage/emulated/0/emlibs/libs/monitor.db’
E/SQLiteDatabase: Failed to open database ‘/storage/emulated/0/baidu/tempdata/ls.db’
以上列举两种异常,主要在小米/红米机子上测试出现,其他机子未测试确认。出现该问题的罪魁祸首就是,在多权限申请当中将原已授权的权限拒绝了,且该权限刚好正被使用。解决以上原因只需要一步“过滤”,遍历mPermissionGroup集合当中每个成员的授权状态再执行多权限申请(如第2点当中写到的)。