权限申请库:AndPermission(二)
在权限申请库:AndPermission(一)中介绍了阿里严振杰所写的权限申请库:AndPermission,虽然很不靠谱被喷的不少,但仍推荐阅读此库的源码。它的源码比较容易阅读和理解,适合Android初学者学习,帮助理解Android的权限机制。
随着Google对Android的不断升级完善,越来越重视对用户隐私的保护,现在Android框架的权限机制已经固定且稳定可靠,用原生框架足够满足开发需求,无需再借助三方的开源库。
废话不多说,直接看源码。从with()入口方法开始,该方法有多个重载,可以接收Context、Fragment、Activity等多种类型作为参数,这样的设计借鉴了Glide。
public static Option with(Context context) {
return new Boot(getContextSource(context));
}
public static Option with(Fragment fragment) {
return new Boot(new XFragmentSource(fragment));
}
public static Option with(android.app.Fragment fragment) {
return new Boot(new FragmentSource(fragment));
}
public static Option with(Activity activity) {
return new Boot(new ActivitySource(activity));
}
返回Option接口类型,该接口定义了若干方法,也是AndPermission库的几个核心功能。
public interface Option {
/**
* Handle runtime permissions.
*/
RuntimeOption runtime();
/**
* Handle request package install permission.
*/
InstallRequest install();
/**
* Handle overlay permission.
*/
OverlayRequest overlay();
/**
* Handle notification permission.
*/
NotifyOption notification();
/**
* Handle system setting.
*/
Setting setting();
}
Option接口唯一实现类是Boot,本篇主要阅读权限申请相关的代码,接着借助runtime()方法返回的Runtime实例进行权限申请操作:
AndPermission.with(this)
.runtime()
.permission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.onGranted(new Action<List<String>>() {
@Override
public void onAction(List<String> data) {
//权限允许
}
})
.onDenied(new Action<List<String>>() {
@Override
public void onAction(List<String> data) {
//权限拒绝
}
})
.rationale(new Rationale<List<String>>() {
@Override
public void showRationale(Context context, List<String> data, RequestExecutor executor) {
executor.execute();
}
})
.start();
上面的主要流程如下图所示(本文流程图均用PlantUML绘制):
通过Runtime的permission(String... permissions)方法进行权限申请,传入需要申请的权限,方法接收不定长String参数,并且返回一个PermissionRequest对象:
@Override
public PermissionRequest permission(@NonNull String... permissions) {
checkPermissions(permissions);
return FACTORY.create(mSource).permission(permissions);
}
FACTORY是用于创建PermissionRequest的工厂,其基本类型是PermissionRequestFactory,工厂的两个实现类分别是:MRequestFactory和LRequestFactory。
public interface PermissionRequestFactory {
/**
* Create permission request.
*/
PermissionRequest create(Source source);
}
这里巧妙的利用了多态设计,在静态代码块中根据Android版本初始化:SDK大于等于23,也就是Android 6.0及以上,使用MRequestFactory;低于23即Android 6.0以下,使用LRequestFactory。这也是Android权限在各版本中最大的区别,详见另一篇《Android Permission 权限总结》。
private static final PermissionRequestFactory FACTORY;
static {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
FACTORY = new MRequestFactory();
} else {
FACTORY = new LRequestFactory();
}
}
现在Android版本绝大多数以Android 6.0及以上为主,占比95.6%以上。本篇仍以Android 6.0以上的权限申请流程为主,Android 6.0以下的权限申请直接过。
这里通过MRequestFactory创建MRequest,最后调用start()方法开启权限请求,这部分流程如下:
接下来,由RequestExecutor执行权限申请任务:
@Override
public void run() {
Context context = mRequest.getSource().getContext();
mMessenger = new Messenger(context, this);
mMessenger.register(getName());
Intent intent = new Intent();
intent.setAction(AndPermission.bridgeAction(context, null));
intent.setPackage(context.getPackageName());
context.bindService(intent, mConnection, Service.BIND_AUTO_CREATE);
}
这一步操作,目的是为了绑定BridgeService,并且构造action标识此次权限申请:{package}.andpermission.bridge。
private static final String ACTION_BRIDGE_SUFFIX = ".andpermission.bridge";
public static String bridgeAction(Context context, String suffix) {
return context.getPackageName() + ACTION_BRIDGE_SUFFIX + (TextUtils.isEmpty(suffix) ? "" : "." + suffix);
}
当BridgeService绑定后,在onServiceConnected()回调中执行下面的流程,通过IBinder执行Service中的权限申请方法:
private void executeCurrent(IBridge iBridge) throws RemoteException {
switch (mRequest.getType()) {
...
case BridgeRequest.TYPE_PERMISSION: {
iBridge.requestPermission(getName(), mRequest.getPermissions());
break;
}
...
}
}
在BridgeService中实现了IBridge接口中的全部方法:
private Stub mStub = new Stub() {
private Source mSource = new ContextSource(BridgeService.this);
public void requestAppDetails(String suffix) throws RemoteException {
BridgeActivity.requestAppDetails(this.mSource, suffix);
}
public void requestPermission(String suffix, String[] permissions) throws RemoteException {
BridgeActivity.requestPermission(this.mSource, suffix, permissions);
}
...
};
可以看到方法中全部都是调用BridgeActivity的静态方法,这些静态方法定义在BridgeActivity类中,申请权限会启动BridgeActivity,AndPermission正是借助BridgeActivity完成权限的申请(整个库是这样的设计思路)。当然这个Activity是不可见的,如同Glide通过给Fragment/Activity插入一个不可见的Fragment,通过监听该Fragment的生命周期来实现对应的请求管理。
/**
* Request for permissions.
*/
static void requestPermission(Source source, String suffix, String[] permissions) {
Intent intent = new Intent(source.getContext(), BridgeActivity.class);
intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_PERMISSION);
intent.putExtra(KEY_PERMISSIONS, permissions);
intent.putExtra(KEY_ACTION_SUFFIX, suffix);
source.startActivity(intent);
}
在BridgeActivity的onCreate(Bundle savedInstanceState)方法,根据传入的参数判别执行哪一项操作。权限申请最终还是通过Activity的requestPermissions()方法完成。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) return;
Intent intent = getIntent();
int operation = intent.getIntExtra(KEY_TYPE, -1);
mActionSuffix = intent.getStringExtra(KEY_ACTION_SUFFIX);
switch (operation) {
case BridgeRequest.TYPE_APP_DETAILS: {
Intent appDetailsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
appDetailsIntent.setData(Uri.fromParts("package", getPackageName(), null));
startActivityForResult(appDetailsIntent, BridgeRequest.TYPE_APP_DETAILS);
break;
}
case BridgeRequest.TYPE_PERMISSION: {
String[] permissions = intent.getStringArrayExtra(KEY_PERMISSIONS);
requestPermissions(permissions, BridgeRequest.TYPE_PERMISSION);
break;
}
...
}
}
并且在onRequestPermissionsResult(...)回执权限申请结果,此部分流程图如下:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Messenger.send(this, mActionSuffix);
finish();
}
最后通过Messenger(继承自BroadcastReceiver),将权限申请结果从BridgeActivity通过广播传递给权限申请者MRequest。这里的action和申请权限时传入的一样。
public static void send(Context context, String suffix) {
Intent broadcast = new Intent(AndPermission.bridgeAction(context, suffix));
context.sendBroadcast(broadcast);
}
再经过层层回调到onAction(T data)将结果传递给开发者,后面这部分流程如下: