一、文档说明
官方文档: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);
}
xianglijiaxing
没有帐号? 立即注册