运行时权限申请

参考官方链接

并没有测试所有机型,官方 ROM 和 小米是 ok 的。
针对:
1. 权限首次申请
2. 被拒绝后的申请
3. Don't ask again 勾选后的申请
三种情形的申请方案。

1. 检查权限

ContextCompat.checkSelfPermission()
e.g. 请求日历的写入权限

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);
// permissionCheck 为 PackageManager.PERMISSION_GRANTED 或 PERMISSION_DENIED 

2. 请求权限

2.1 解释应用为什么需要权限

ActivityCompat.shouldShowRequestPermissionRationale(Activity activity, String permission)
如果请求的权限用户已经拒绝过,但没有点击 “Don't ask again”,就会返回 true,表示需要显示 权限请求解释提示
如果用户点击了 Don't ask again,是返回 false 的,还需要配合权限检查为 DENIED,才说明该权限 为 Don't ask again

2.2 请求权限

ActivityCompat.requestPermissions(Activity activity, String[] permissions,int requestCode);

e.g.

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.
    } else {
        // No explanation needed, we can request the permission.
        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

3. 处理权限请求响应

通过onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)方法来判断权限请求的结果
形参中 permissionsgrantResults 是一一对应的,被拒绝的权限可以通过shouldShowRequestPermissionRationale方法来判断是否为 Dont ask again 从而提示用户手动前往应用设置页开启

e.g.

@Override
public void onRequestPermissionsResult(int requestCode,
        String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }
        // other 'case' lines to check for other
        // permissions this app might request
    }
}



附1:一个简单的 demo

/**
 * 
 *     @author Jiun
 *     @date  :2018/09/05/18:58
 *     desc   : 文件描述
 *     version: 当前版本号
 * 
*/ public class PermissionHandler { private static final int REQ_CODE = 0x10; private WeakReference mActivityWeakReference; private static final String[] CAMERA = new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}; private String[] mPermissionArrRequest; /** * 可用来记录最新申请的权限,进行更细致的判断处理,暂时未用到 */ private String mPermissionLatestRequest; private PermissionHandler(Activity activity, @NonNull String[] permissions) { mActivityWeakReference = new WeakReference<>(activity); mPermissionArrRequest = permissions; mPermissionLatestRequest = ""; } public static PermissionHandler getInstance(Activity activity, String[] permissions) { return new PermissionHandler(activity, permissions); } public static PermissionHandler getCameraInstance(Activity activity) { return new PermissionHandler(activity, CAMERA); } public boolean checkPermission() { final Activity activity = mActivityWeakReference.get(); if (activity == null) { return false; } for (String permission : mPermissionArrRequest) { mPermissionLatestRequest = permission; if (isNotGranted(activity, permission)) { if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { DialogUtil.showConfirmDialog(activity, "请授权下面的权限申请,否则该功能可能无法使用", new DialogUtil.OnDialogConfirmClickListener() { @Override public void onConfirm(Dialog dialog) { ActivityCompat.requestPermissions(activity, mPermissionArrRequest, REQ_CODE); dialog.cancel(); } }).show(); } else { ActivityCompat.requestPermissions(activity, mPermissionArrRequest, REQ_CODE); } return false; } } return true; } /** * 需要在 Activity#onRequestPermissionResult() 中调用此方法 */ public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case REQ_CODE: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0) { for (int i = 0; i < grantResults.length; i++) { int grantResult = grantResults[i]; if (grantResult == PackageManager.PERMISSION_GRANTED) { // TODO(Hjy): 2018/9/5 授权成功,比如说 在 checkPermission 中把未授权的权限保存起来,在授权结果中进行比对,看是否全部授权成功,来设置授权的回调 } else if (permissions.length > i) { if (checkIsDeniedForeverAndShowDialog(permissions[i])) { return; } } } } else { for (String permission : permissions) { if (checkIsDeniedForeverAndShowDialog(permission)) { return; } } } return; } default: } } private boolean checkIsDeniedForeverAndShowDialog(String permission) { final Activity activity = mActivityWeakReference.get(); if (activity == null) { return true; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isDeniedForever(activity, permission)) { DialogUtil.showCommonDialog(activity, "权限申请失败或被拒绝,请手动前往授权", new DialogUtil.OnDialogConfirmClickListener() { @Override public void onConfirm(Dialog dialog) { dialog.dismiss(); startAppDetailsSettings(activity); } }).show(); return true; } return false; } private boolean isNotGranted(Activity activity, String permission) { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(activity, permission); } @RequiresApi(api = Build.VERSION_CODES.M) private boolean isDeniedForever(Activity activity, String permission) { return isNotGranted(activity, permission) && !activity.shouldShowRequestPermissionRationale(permission); } /** * 打开系统设置界面 */ private void startAppDetailsSettings(Activity activity) { Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS"); intent.setData(Uri.parse("package:" + activity.getPackageName())); ComponentName componentName = intent.resolveActivity(activity.getPackageManager()); if (componentName != null) { activity.startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } } }

使用步骤(请求相机用到的权限为例):

  1. 在合适的地方获取实例
PermissionHandler mPermissionHandler = PermissionHandler.getCameraInstance(activity);
  1. 权限检查并请求
if (mPermissionHandler.checkPermission()) {
  // 打开相机拍照
}
  1. 获取 权限请求回调结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (mPermissionHandler != null) {
        mPermissionHandler.onRequestPermissionResult(requestCode, permissions, grantResults);
    }
}

附2:危险权限和权限组

权限组 权限
CALENDAR READ_CALENDAR
WRITE_CALENDAR
CAMERA CAMERA
CONTACTS READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS`
LOCATION ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION`
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE

附3:如何区分 i.e. 和 e.g.

上学的时候就很好奇,终于想起来搜搜了:joy:

1. 理解含义:
"i.e."是拉丁语 id est 的缩写,意为"that is"(即), "e.g." 是拉丁语exempli gratia 的缩写,意为"for the sake of example"(如例子)。

2. 把用法和好记的短语联系起来:
记拉丁语可能比较难,就把"i.e."记为 "in essence"(本质上)、 "in other words"(换句话), "e.g." 记成 "example given"(给出例子) 。

3. 使用"i.e."来作解释:
"i.e." 是用来在一句话之后换种方式解释或描述之前说的内容。
The elephant is a pachyderm, i.e., an animal with thick skin and nails resembling hooves.(大象是厚皮类动物,即皮肤厚、有蹄子的动物)
I went to my least favorite place (i.e., the dentist).(我去我最喜欢的地方了,即牙医诊所)

4. 注意:
"i.e."之后一般的是延伸的定义,也可以是个比喻。如果你把 "i.e." 替换为 "in other words"句子就通顺,如果是 "for example" 就不行了。

5. 试着把简写换成原意,听起来通顺的话就可能用对了:
比如 "I like quiet activities (e.g., reading)"(我喜欢安静的活动(例如阅读)), "I like quiet activities (for example, reading)". 如果使用 i.e.,则用 "in other words"比 "that is."更好用。

6. 在一个或多个例子前使用 "e.g." 想想 "e.g."后面要接的东西作为一个类别, 然后看看这个类别里可以放什么东西:
Buy some vegetables, e.g., carrots.(买点蔬菜,比如胡萝卜)
I like power metal (e.g., Firewind, Iced Earth, Sonata Arctica).(我喜欢能量金属乐,例如焚风乐队、冰封大地乐队、极光奏鸣曲乐团)
注意这里"i.e."说出来不通顺。 "Carrots"(胡萝卜)不是所有的蔬菜,而只是一种而已。如果要用 "i.e.",你可以说 "Buy some vegetables, i.e., the edible part of any plant."(买点蔬菜,即植物可食部分)同样地,乐队名也只是乐曲分格的一部分,如果要用 "i.e." 则"I like power metal, i.e., fast metal with symphonic elements and epic themes."(我喜欢能量金属乐,即快速金属乐搭配交响乐元素和史诗主题的乐曲风格)

7. 两者都用括号或逗号:
要指示分开的从句,可以前面加个逗号,或者用括号括起来,两者上述例子都用到。如果用括号要在两个缩写前就用上,写出相应的例子或解释以后把括号关上。

美式英语中,在 "i.e."、 "e.g."后加个逗号,英式则不用加。

小提示

  • 还是担心用法用错,则最好不用它们,甚至写作中也不用。你要表示“例如”时,就写“for example”,表示“即”的时候,用“that is”。这样也不会多写多少,也不出错。
  • 更好的"i.e." 和 "e.g."对比的例子,就是 Chili Palmer (John Travolta) 和Ray "Bones" Barboni (Dennis Farina)在 1995 电影 Get Shorty (《黑道当家》)里的对白
  • 没必要在"e.g." 后的一长串行表中使用 "etc." ,因为"e.g." 本身就表示不完整的列表。
  • 说话的时候最好不用"i.e." 、 "e.g." ,而说"that is" 、 "in other words" 代表"i.e." , "for example" 、 "for instance" 代表 "e.g."

你可能感兴趣的:(运行时权限申请)