App开发过程中,涉及到硬件设备的操作,比如拍照、录音、定位等等,都要在AndroidManifest.xml中声明相关的权限。可是Android系统为了防止某些App滥用权限,从而允许用户在系统设置里面对App禁用某些权限。然而这又带来另一个问题,用户打开App之后,App可能因为权限不足导致无法正常运行,甚至直接崩溃闪退。遇到这种情况,只需用户在系统设置中开启相关权限即可恢复正常,但是用户并非专业的开发者,他怎知要去启用哪些权限呢?再说,每次都要用户亲自打开系统设置页面,再琢磨半天精挑细选那些必须开启的权限,不但劳力而且劳神,这种用户体验实在差劲。 有鉴于此,Android从6.0开始引入了运行时权限管理机制,允许App在运行过程中动态检查是否拥有某项权限,一旦发现缺少某种必需的权限,则系统会自动弹出小窗提示用户去开启该权限。如此这般,一方面开发者无需担心App因权限不足而闪退的问题,另一方面用户也不再头痛是哪个权限被禁止导致App用不了的毛病,这个贴心的动态权限授权功能可谓是皆大欢喜。下面就来看看如何在代码中实现运行时权限管理机制。 首先要检查Android系统是否为6.0及以上版本,因为运行时权限管理机制是6.0才开始支持的功能。其次调用ContextCompat.checkSelfPermission方法,检查检查当前App是否开启了指定的权限。倘若检查结果是尚未开启权限,则再调用ActivityCompat.requestPermissions方法,请求系统弹出开启权限的确认对话框。详细的权限校验代码如下所示:
// 检查某个权限。返回true表示已启用该权限,返回false表示未启用该权限
public static boolean checkPermission(Activity act, String permission, int requestCode) {
Log.d(TAG, "checkPermission: "+permission);
boolean result = true;
// 只对Android6.0及以上系统进行校验
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 检查当前App是否开启了名称为permission的权限
int check = ContextCompat.checkSelfPermission(act, permission);
if (check != PackageManager.PERMISSION_GRANTED) {
// 未开启该权限,则请求系统弹窗,好让用户选择是否立即开启权限
ActivityCompat.requestPermissions(act, new String[]{permission}, requestCode);
result = false;
}
}
return result;
}
比如App现在准备拍照,那么需要检查是否开启了相机权限Manifest.permission.CAMERA,如果没有启用相机权限,则系统会弹出下图所示的选择窗口。
再比如App准备获取手机的位置信息,那么需要检查是否开启了定位权限Manifest.permission.ACCESS_FINE_LOCATION,如果没有启用定位,则系统会弹出下图所示的选择窗口。
注意到系统的权限选择弹窗存在“拒绝”和“允许”两个按钮,这便意味着开发者要对两种选项分别进行处理。如果用户点击“拒绝”按钮,自然表示接下来App将会无法正常运行,此时需要提示用户可能产生的问题及其原因;如果用户点击“允许”按钮,系统会立即给App赋予相应的权限,那么App就按照正常的流程走下去,该拍照就拍照、该定位就定位。以上的选项判断逻辑,具体到代码中则需重写Activity的onRequestPermissionsResult函数,重写后的函数代码示例如下:
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == mRequestCode) { // 通过requestCode区分不同的请求
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 已授权,则进行后续的正常逻辑处理
} else {
Toast.makeText(this, "需要允许相机权限才能拍照噢", Toast.LENGTH_SHORT).show();
}
}
}
有时某种业务必须同时开启多项权限,譬如录像就得既开启相机权限、又开启录音权限。那么在校验权限的时候,要多次调用ContextCompat.checkSelfPermission方法,只有待检查的所有权限都已经授权,才无需系统弹窗提示;否则的话,仍需系统逐个弹窗以供用户选择确认。下面是同时校验多个权限的代码例子,其中多个权限以字符串数组的参数形式传入“new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}”:
public static boolean checkMultiPermission(Activity act, String[] permissions, int requestCode) {
boolean result = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int check = PackageManager.PERMISSION_GRANTED;
// 通过权限数组检查是否都开启了这些权限
for (String permission : permissions) {
check = ContextCompat.checkSelfPermission(act, permission);
if (check != PackageManager.PERMISSION_GRANTED) {
break;
}
}
if (check != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(act, permissions, requestCode);
result = false;
}
}
return result;
}
仍以录像业务为例,假如之前App既无相机权限也无录音权限,则运用了运行时权限管理机制之后,系统会在界面上依次弹出录音权限选择窗、相机权限选择窗。两个权限弹窗的截图如下所示: 录音权限选择窗
相机权限选择窗