一、文档说明
官方文档:https://developer.android.com/training/permissions/requesting.html#explain
https://commonsware.com/blog/2015/08/17/random-musings-android-6p0-sdk.html
http://jijiaxin89.com/2015/08/30/Android-s-Runtime-Permission/
系统对话窗只会展示请求权限对应的权限组,而不会显示具体的权限。
当我们在targetSdkVersion 低于23的app调用一个需要权限的函数时,这个权限如果被用户取消授权了的话,不抛出异常。但是他将啥都不干,结果导致函数返回值是null或者0.
二、基本使用
一般情况
在
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
结果为-1的情况下,
如果权限被拒绝过一次(对话框拒绝或者设置中取消权限),下一次请求就会出现“不再提示”的勾选框;
如果是第一次请求权限,或者权限被拒绝,且勾选了”不再提示“,那么调用
ActivityCompat.shouldShowRequestPermissionRationale()
将返回false;仅仅是上一次否定的情况下返回true。
示例代码如下:
if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) { showMessageOKCancel("You need to allow access to Contacts", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { requestPermissions(new String[] {Manifest.permission.WRITE_CONTACT,REQUEST_CODE_ASK_PERMISSION); } }); }else{ requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},REQUEST_CODE_ASK_PERMISSIONS); }
如果上一次权限被拒绝,且勾选了”不再提示“,则下一次调用
ActivityCompat.requestPermissions()
请求权限,不会出现系统对话框
直接callback结果为-1
特殊情况:
在手机app权限管理界面中,如果更改了手机的权限,则应用会直接被杀死
①授权 --->非授权: 再次请求权限相当于否定了一次权限请求
②非授权---> 授权: 权限直接获取到了
③非授权--->授权 --->非授权:再次请求权限相当于第一次请求
三、开源解决方案
github开源库:
https://github.com/hotchemi/PermissionsDispatcher
https://github.com/googlesamples/easypermissions
https://github.com/hongyangAndroid/MPermissions
开源方案对比:
http://www.jianshu.com/p/07e1a1cff5ad
四、动态权限的坑
1.蓝牙扫描需要位置权限:
<!-- Android6.0 蓝牙扫描才需要-->
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
BluetoothUtils: Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan res
2.Fragment中请求权限:
两种解决方案
在fragment如果使用ActivityCompat.requestPermissions,不会调用onRequestPermissionsResult(),这个大家应该都知道吧,千万注意我遇到的问题,记得在activity的onRequestPermissionsResult()调用super,不然也不会回调
---
在Fragment中申请权限,不要使用ActivityCompat.requestPermissions, 直接使用Fragment的requestPermissions方法,否则会回调到Activity的onRequestPermissionsResult,另外在fragment中询问的方法也可以直接使用fragment中的方法shouldShowRequestPermissionRationale方法。
---
Fragment嵌套Fragment请求权限的问题:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); List<Fragment> fragments = getChildFragmentManager().getFragments(); if (fragments != null) { for (Fragment fragment : fragments) { if (fragment != null) { fragment.onRequestPermissionsResult(requestCode,permissions,grantResults); } } } }
3.WRITE_SETTINGS权限怎么处理
/** * An app can use this method to check if it is currently allowed to write or modify system * settings. In order to gain write access to the system settings, an app must declare the * {@link android.Manifest.permission#WRITE_SETTINGS} permission in its manifest. If it is * currently disallowed, it can prompt the user to grant it this capability through a * management UI by sending an Intent with action * {@link android.provider.Settings#ACTION_MANAGE_WRITE_SETTINGS}. * * @param context A context * @return true if the calling app can write to system settings, false otherwise */ if(!Settings.System.canWrite(this)){ Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_CODE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE) { if (Settings.System.canWrite(this)) { //检查返回结果 Toast.makeText(MainActivity.this, "WRITE_SETTINGS permission granted", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "WRITE_SETTINGS permission not granted", Toast.LENGTH_SHORT).show(); } } }
4.悬浮窗权限问题:
需要动态的引导用户去设置
if (!Settings.canDrawOverlays(this)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE); }
这个可以参考
五、危险权限组
https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
This video will give you a better idea about UX, handling Runtime permissions
https://www.youtube.com/watch?v=iZqDdvhTZj0
--------------------------------------------------------------------------------------------------------------
额外代码
命令:
adb shell dumpsys activity top
各大厂商手机调转至应用的权限设置界面:
参考:https://github.com/hkq325800/JumpPermissionManagement
/** * Build.MANUFACTURER */ private static final String MANUFACTURER_HUAWEI = "Huawei";//华为 private static final String MANUFACTURER_MEIZU = "Meizu";//魅族 private static final String MANUFACTURER_XIAOMI = "Xiaomi";//小米 private static final String MANUFACTURER_SONY = "Sony";//索尼 private static final String MANUFACTURER_OPPO = "OPPO"; private static final String MANUFACTURER_LG = "LG"; private static final String MANUFACTURER_VIVO = "vivo"; private static final String MANUFACTURER_SAMSUNG = "samsung";//三星 private static final String MANUFACTURER_LETV = "Letv";//乐视 private static final String MANUFACTURER_ZTE = "ZTE";//中兴 private static final String MANUFACTURER_YULONG = "YuLong";//酷派 private static final String MANUFACTURER_LENOVO = "LENOVO";//联想
public static void Huawei(Activity activity) { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity"); intent.setComponent(comp); activity.startActivity(intent); } public static void Meizu(Activity activity) { Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); activity.startActivity(intent); } // 如何获取MIUI的版本??? // http://blog.csdn.net/devilkin64/article/details/19415717 // public static void Xiaomi(Activity activity) { // Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); // ComponentName componentName = new ComponentName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); // intent.setComponent(componentName); // intent.putExtra("extra_pkgname", BuildConfig.APPLICATION_ID); // activity.startActivity(intent); // } public static void Xiaomi(Context activity) { Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); ComponentName componentName = new ComponentName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity"); intent.setComponent(componentName); intent.putExtra("extra_pkgname", activity.getPackageName()); activity.startActivity(intent); } public static void Sony(Activity activity) { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); ComponentName comp = new ComponentName("com.sonymobile.cta", "com.sonymobile.cta.SomcCTAMainActivity"); intent.setComponent(comp); activity.startActivity(intent); } public static void OPPO(Activity activity) { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); ComponentName comp = new ComponentName("com.color.safecenter", "com.color.safecenter.permission.PermissionManagerActivity"); intent.setComponent(comp); activity.startActivity(intent); } public static void LG(Activity activity) { Intent intent = new Intent("android.intent.action.MAIN"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); ComponentName comp = new ComponentName("com.android.settings", "com.android.settings.Settings$AccessLockSummaryActivity"); intent.setComponent(comp); activity.startActivity(intent); } public static void Letv(Activity activity) { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); ComponentName comp = new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.PermissionAndApps"); intent.setComponent(comp); activity.startActivity(intent); } /** * 只能打开到自带安全软件 * @param activity */ public static void _360(Activity activity) { Intent intent = new Intent("android.intent.action.MAIN"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); ComponentName comp = new ComponentName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity"); intent.setComponent(comp); activity.startActivity(intent); } public static void Smartisan(Context activity) { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); ComponentName comp = new ComponentName("com.smartisanos.security", "com.smartisanos.security.MainActivity"); intent.setComponent(comp); activity.startActivity(intent); } /** * 应用信息界面 * @param activity */ public static void ApplicationInfo(Activity activity){ Intent localIntent = new Intent(); localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= 9) { localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); localIntent.setData(Uri.fromParts("package", activity.getPackageName(), null)); } else if (Build.VERSION.SDK_INT <= 8) { localIntent.setAction(Intent.ACTION_VIEW); localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails"); localIntent.putExtra("com.android.settings.ApplicationPkgName", activity.getPackageName()); } activity.startActivity(localIntent); }
没有帐号? 立即注册