Android5.0后加入了权限动态申请,而且过运营商测试也有这个要求。但是真正在所需权限的地方写申请权限代码的话工作量是很大的(也有app真的能做到这种要求,例如微信,入口处什么权限都不需要的,只有具体需要才申请,用户体验真是做到极致了),所以app一般会在入口处申请全部的权限,例如新闻客户端就可这么搞,反正就一个入口。但是如果Activity非常多,入口也很多,那么要写大量的申请权限代码。Contacts和Dialer中为了解决这个问题专门写了几个类在ContactsCommon中。
RequestPermissionsActivityBase
packages/apps/ContactsCommon/src/com/android/contacts/common/activity/RequestPermissionsActivityBase.java
private Intent mPreviousActivityIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPreviousActivityIntent = (Intent) getIntent().getExtras().get(PREVIOUS_ACTIVITY_INTENT);
// Only start a requestPermissions() flow when first starting this activity the first time.
// The process is likely to be restarted during the permission flow (necessary to enable
// permissions) so this is important to track.
if (savedInstanceState == null) {
requestPermissions();
}
}
创建的时候保存上一个Acitivity(也就是要申请权限的Activity),然后申请权限
private void requestPermissions() {
...
final ArrayList<String> unsatisfiedPermissions = new ArrayList<>();
for (String permission : getDesiredPermissions()) {
if (checkSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
unsatisfiedPermissions.add(permission);
}
}
requestPermissions(
unsatisfiedPermissions.toArray(new String[unsatisfiedPermissions.size()]),
PERMISSIONS_REQUEST_ALL_PERMISSIONS);
...
}
首先获取要申请的权限,然后申请。其中getDesiredPermissions是个抽象函数,要在子类中实现。
public void onRequestPermissionsResult(int requestCode, String permissions[],
int[] grantResults) {
if (permissions != null && permissions.length > 0
&& isAllGranted(permissions, grantResults)) {
mPreviousActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(mPreviousActivityIntent);
finish();
overridePendingTransition(0, 0);
...
}
获取到权限后,关闭当前activity然后跳转回原来的Activity。
该类中还有个protected静态方法供子类调用
protected static boolean startPermissionActivity(Activity activity,
String[] requiredPermissions, Class<?> newActivityClass) {
...
if (!RequestPermissionsActivity.hasPermissions(activity, requiredPermissions)) {
final Intent intent = new Intent(activity, newActivityClass);
intent.putExtra(PREVIOUS_ACTIVITY_INTENT, activity.getIntent()); //简化了需要用到该类的代码,例如调用方根本无需知道(PREVIOUS_ACTIVITY_INTENT是啥
activity.startActivity(intent);
activity.finish();
return true;
}
...
return false;
}
RequestPermissionsActivity
packages/apps/ContactsCommon/src/com/android/contacts/common/activity/RequestPermissionsActivity.java
@Override
protected String[] getDesiredPermissions() {
return new String[]{
permission.ACCESS_FINE_LOCATION, // Location Group
permission.READ_CONTACTS, // Contacts group
permission.READ_CALL_LOG, // Permission group phone
permission.READ_CALENDAR, // Calendar group
permission.READ_SMS, // SMS group
};
}
实现了基类的抽象方法,因为这个是给Contacts用的,所以已经初始化为Contacts需要的权限。
public static boolean startPermissionActivity(Activity activity) {
return startPermissionActivity(activity, REQUIRED_PERMISSIONS,
RequestPermissionsActivity.class);
}
startPermissionActivity调用基类的静态方法完成申请权限的任务,可以看出startPermissionActivity就是给Contacts中其它Activity使用的,使用只需要一句
RequestPermissionsActivity.startPermissionActivity(this);
虽然这个是给Contacts使用的,但是稍加修改就能作为一个申请权限的模板代码使用,不用每个类写一遍申请权限代码。
PermissionsUtil
packages/apps/ContactsCommon/src/com/android/contacts/common/util/PermissionsUtil.javapublic static boolean hasPermission(Context context, String permission) {
return context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
}
public static boolean hasAppOp(Context context, String appOp) {
final AppOpsManager appOpsManager =
(AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
final int mode = appOpsManager.checkOpNoThrow(appOp, Process.myUid(),
context.getPackageName());
return mode == AppOpsManager.MODE_ALLOWED;
}
PermissionsUtil是个工具类,最常用的就是上面两个了。
该类中的意外发现是LocalBroadcastManager,详细见点击打开链接,它只会在进程内广播,不会影响别的进程(也就不受别的进程的影响),代码中发送消息的又一个工具。
其它
其实app使用系统签名就不会弹出权限申请框而自动有权限了。Android.mk中LOCAL_CERTIFICATE := platform