Android6.0中对权限的处理
Android6.0带来了新的权限管理方式,对于开发者来说,兼容android6.0是必须要做的事情了。本文的主要目的是展示一下Android的系统应用中对权限是怎么处理的。
一.Using Permission(使用权限)
一个新建的Android应用默认是没有权限的,这意味着它不能执行任何可能对用户体验有不利影响的操作或者访问设备数据。为了使用受保护的功能,你必须包含一个或者多个标签在你的app manifest中。
Android 6.0中权限分为两种,普通权限和危险权限(即运行时权限,下面统称运行时权限)。
1.1普通权限
如果你的应用manifest中只申明了普通权限(也就是说,这些权限对于用户隐私和设备操作不会造成太多危险),系统会自动授予这些权限。
1.2运行时权限
如果你的应用manifest中声明了运行时权限(也就是说,这些权限可能会影响用户隐私和设备的普通操作),系统会明确的让用户决定是否授予这些权限。系统请求用户授予这些权限的方式是由当前应用运行的系统版本来决定的。
1.2.1 Android6.0及以上的系统
如果你的设备运行的是Android6.0(API level 23)及以上的系统,并且你的应用的targetSdkVersion也是23或者更高,那么应用向用户请求这些权限是实时的。这意味着用户可以随时取消这些运行时权限的授权。所以应用在每次需要用到这些运行时权限的时候都需要去检查是否还有这些权限的授权。
1.2.2 Android 5.1及以下的系统
如果你的设备运行在Android5.1(API level 22)及以下的系统中,或者你的app的targetSdkVersion是22或者更低。系统会请求用户在apk安装的时候授予这些权限。
如果你的应用更新的时候添加了一个权限,系统会在用户更新应用的时候请求用户授予这个权限,一旦用户安装了这个应用,唯一可以取消授权的方式就是卸载掉这个应用。
如果app的targetSdkVersion是22或者以下,但是运行在android 6.0或以上版本的手机中,会发生什么?
安装过程中,会一起请求用户授予所有权限,如果用户拒绝,將不能安装这个app,只有用户全部同意这些授权,才能安装这个应用,但是问题来了,安装好了这个应用之后,android6.0以上的系统中,用户是可以去设置中取消授权的,而且是随时都可以取消,所以很多运行时权限可能也得不到,目前官方的做法是,如果用户取消该项授权,那么依赖该项授权的方法的返回值为null,所以你的app可能会报空指针异常。以后是否会针对22以下的app做改变还不得而知,毕竟crash是很难让人接受的,但是crash是由用户造成的,用户应该也可以理解。
二.Dangerous Permissions:
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGE
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
可以通过adb shell pm list permissions -d -g进行查看。
下面直接贴出源码中一段封装权限申请的代码:
public class RequestPermissionsActivity extends Activity {
private static final String TAG = "RequestPermissionsActivity";
private static final String PREVIOUS_INTENT = "previous_intent";
private static final int REQUEST_ALL_PERMISSIONS = 1;
private Intent mPreviousIntent;
private static final String[] REQUIRED_PERMISSIONS = new String[] {
permission.READ_EXTERNAL_STORAGE,
permission.WRITE_EXTERNAL_STORAGE };
public static boolean startPermissionActivity(Activity activity) {
return startRequestPermissionActivity(activity,REQUIRED_PERMISSIONS,
RequestPermissionsActivity.class);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPreviousIntent = (Intent) getIntent().getExtras().get(
PREVIOUS_INTENT);
if (savedInstanceState == null) {
requestPermissions();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantedResults) {
if (permissions != null && permissions.length > 0
&& arePermissionsGranted(permissions, grantedResults)) {
mPreviousIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(mPreviousIntent);
finish();
} else {
Toast.makeText(this, "You have disabled a required permission",Toast.LENGTH_SHORT).show();
finish();
}
}
protected static boolean startRequestPermissionActivity(Activity activity,
String[] requiredPermissions, Class<?> newActivityClass) {
if (!RequestPermissionsActivity.checkPermissions(activity,
requiredPermissions)) {
final Intent intent = new Intent(activity, newActivityClass);
intent.putExtra(PREVIOUS_INTENT, activity.getIntent());
activity.startActivity(intent);
activity.finish();
return true;
}
return false;
}
private static boolean checkPermissions(Context context, String[] permissions) {
for (String permission : permissions) {
if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
private boolean arePermissionsGranted(String permissions[], int[] grantResult) {
for (int i = 0; i < permissions.length; i++) {
if (grantResult[i] != PackageManager.PERMISSION_GRANTED
&& Arrays.asList(REQUIRED_PERMISSIONS).contains(permissions[i])) {
return false;
}
}
return true;
}
private void requestPermissions() {
final ArrayList<String> noGrantedPermissions = new ArrayList<>();
for (String permission : REQUIRED_PERMISSIONS) {
if (checkSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
noGrantedPermissions.add(permission);
}
}
if (noGrantedPermissions.size() == 0) {
Log.e(TAG, "Request permission activity was called even"
+ " though all permissions are satisfied.");
}
requestPermissions(noGrantedPermissions.toArray(new String[noGrantedPermissions.size()]),
REQUEST_ALL_PERMISSIONS);
}
}
调用方式非常简单,在你的MainActivity的Oncreate()中加上如下代码:
protected void onCreate(Bundle state) {
super.onCreate(state);
if (RequestPermissionsActivity.startPermissionActivity(this)) {
finish();//这里可以换成你自己的逻辑,如果你的App不是必须要这些权限的话
return;
}
}
if里面应该还加上Build.VERSION.SDK_INT>= Build.VERSION_CODES.M判断。解释一下,这里为什么有个return。这里的return不是多余的,因为执行finish( )是一个异步过程,你的Oncreate方法并不会立马终止,试着打下log就会发现你的Oncreate方法中接下来的代码会继续执行,所以如果你下面的代码中有显示Dialog这样的操作时,你的程序就会抛异常了,这时return就能起到一个很好的作用。
相关API的使用就不一一述说了,看一下方法名就知道这个方法是干什么用的了,这里主要是体现如何快速的去使用RequestPermissionsActivity 这个类去兼容6.0,只要把RequestPermissionsActivity的REQUIRED_PERMISSIONS数组 里面的权限换成你自己需要申请的权限,就可以在第一次启动的时候一次性去向用户申请你所有需要用到的危险权限。
当然,如果你需要申请的危险权限很多,并且不是全部都必要的话,也可以把这个RequestPermissionsActivity设为抽象类,在需要调用的地方去实现。
在Android6.0源码中的拨号和联系人模块里面就是这样做的,将RequestPermissionsActivity设为一个抽象类,然后在几个需要申请权限的地方去分别申请权限。
第一次写博客呀,欢迎大家指出错误,谢谢!