您的位置:首页 > 移动开发 > Android开发

Android 6.0 运行时权限处理问题

2016-09-11 19:28 429 查看


自从升级到Android M以来,最大的改变就是增加了运行时权限RuntimePermission,6.0以上的系统如果没有做适配,运行了targetSDK=23的App时就会报权限错误。我们知道6.0以下的系统是按照的时候权限申请的,6.0和之后的版本是我们想要使用某个app的权限,去动态申请的,这也是基于安全上的考虑吧(比如:单机的象棋对战,请求访问通讯录权限等不合理的权限,这肯定是有问题的)。

为了保护用户的隐私,谷歌官方将权限分为了两类,一个是正常权限(Normal Permissions),这类权限不涉及用户隐私,是不需要用户进行授权的,比如访问网络,手机震动等。还有一类是危险权限(Dangerous Permissions),一般是涉及到用户隐私的,需要用户进行授权,比如操作SD卡的写入,相机,录音等。

我们来看一张权限的清单文件:



我们可以通过adb shell pm list permissions -d -g进行查看。

权限如何申请

那么对于我们开发者来说,怎么适配6.0呢?按着官方的api走就行:

1,在AndroidManifest文件中添加需要的权限。

2,检查权限

if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
}else{
//
}

建议这些检查权限的代码可以写到基类里面去。

3,申请授权

ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);

说明:第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以第二个参数看出,6.0是一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。

4,处理权限申请回调

@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;
}
}
}

不过这里有个情况,对于用户上次拒绝的权限,在下次需要这个权限的时候,系统怎么处理的了?

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.

}

所以完整的处理逻辑:

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.
}
}

项目实践

权限工具类:

public class PermissionUtils {

private static final ArrayMap<String, Integer> MIN_SDK_PERMISSIONS;

static {
MIN_SDK_PERMISSIONS = new ArrayMap<>(8);
MIN_SDK_PERMISSIONS.put("com.android.voicemail.permission.ADD_VOICEMAIL", 14);
MIN_SDK_PERMISSIONS.put("android.permission.BODY_SENSORS", 20);
MIN_SDK_PERMISSIONS.put("android.permission.READ_CALL_LOG", 16);
MIN_SDK_PERMISSIONS.put("android.permission.READ_EXTERNAL_STORAGE", 16);
MIN_SDK_PERMISSIONS.put("android.permission.USE_SIP", 9);
MIN_SDK_PERMISSIONS.put("android.permission.WRITE_CALL_LOG", 16);
MIN_SDK_PERMISSIONS.put("android.permission.SYSTEM_ALERT_WINDOW", 23);
MIN_SDK_PERMISSIONS.put("android.permission.WRITE_SETTINGS", 23);
}

private static volatile int targetSdkVersion = -1;

public static boolean checkPermissions(int... grantResults) {
if (grantResults.length == 0) {
return false;
}
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}

public static boolean hasSelfPermissions(Context context, String... permissions) {
for (String permission : permissions) {
if (permissionExists(permission) && !hasSelfPermission(context, permission)) {
return false;
}
}
return true;
}

private static boolean permissionExists(String permission) {
Integer minVersion = MIN_SDK_PERMISSIONS.get(permission);
return minVersion == null || Build.VERSION.SDK_INT >= minVersion;
}

private static boolean hasSelfPermission(Context context, String permission) {
try {
return PermissionChecker.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
} catch (RuntimeException t) {
return false;
}
}

public static boolean shouldShowRequestPermissionRationale(Activity activity, String... permissions) {
for (String permission : permissions) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
return true;
}
}
return false;
}

@TargetApi(Build.VERSION_CODES.DONUT)
public static int getTargetSdkVersion(Context context) {
try {
if (targetSdkVersion != -1) {
return targetSdkVersion;
}
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
targetSdkVersion = packageInfo.applicationInfo.targetSdkVersion;
} catch (PackageManager.NameNotFoundException ignored) {
}
return targetSdkVersion;
}
}

这里可以根据实际情况进行优化和扩展

基类:

public class BasePermissionActivity extends AppCompatActivity {

private PermissionHandler mHandler=null;
private static int requesrCode=001;

/**
* 请求权限
*/
protected void requestPermission(String[] permissions, PermissionHandler handler) {
if (PermissionUtils.hasSelfPermissions(this, permissions)) {
handler.onGranted();
} else {
mHandler = handler;
ActivityCompat.requestPermissions(this, permissions, requesrCode);
}
}

protected void requestPermission( PermissionHandler handler,String permissions) {
if (PermissionUtils.hasSelfPermissions(this, permissions)) {
handler.onGranted();
} else {
mHandler = handler;
ActivityCompat.requestPermissions(this, new String[]{permissions} , requesrCode);
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (mHandler == null) return;
if (PermissionUtils.checkPermissions(grantResults)) {
mHandler.onGranted();
} else {
if (!PermissionUtils.shouldShowRequestPermissionRationale(this, permissions)) {
if (!mHandler.onNeverRequest()) {
Toast.makeText(this, "权限已被拒绝,请在设置-应用-权限中打开", Toast.LENGTH_SHORT).show();
}

} else {
mHandler.onDenied();
}
}
}

public abstract class PermissionHandler {
//权限通过
public abstract void onGranted();

//权限拒绝
public void onDenied() {
}

//不再询问
public boolean onNeverRequest() {
return false;
}
}
}

测试:

public class MainActivity extends BasePermissionActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

init();
}

private void init() {
request1();
}

private void request1() {
requestPermission(new String[]{Manifest.permission.CAMERA}, new PermissionHandler() {
@Override
public void onGranted() {
Intent intent = new Intent(); //调用照相机
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
startActivity(intent);
}

@Override
public void onDenied() {
Toast.makeText(MainActivity.this, "拒绝", Toast.LENGTH_SHORT).show();
}
});
}

private void request2() {
requestPermission(new String[]{Manifest.permission.CAMERA}, new PermissionHandler() {
@Override
public void onGranted() {
Intent intent = new Intent(); //调用照相机
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
startActivity(intent);
}

@Override
public void onDenied() {
Toast.makeText(MainActivity.this, "拒绝", Toast.LENGTH_SHORT).show();
}
});
}
}
代码:https://github.com/xiangzhihong/permissionDemo

最后附上鸿洋封装的比较好的库:https://github.com/lovedise/PermissionGen
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: