【源码阅读】AndPermission源码阅读

秒速五厘米 2022-10-01 10:52 477阅读 0赞

前言

权限是绝大多数App必不可少的部分,不管你仍在用原生的方式,还是其他的开源库,AndPermission绝对是值得学习的一个开源库,今天,我们就来学习下它的设计思想。

AndPermission

思路

权限库的思路大体上都如下图所示,也玩不出太复杂的花样。

使用

  • 1.添加引用 implementation 'com.yanzhenjie.permission:support:2.0.1'
  • 2.请求权限

    AndPermission.with(this)

    1. .runtime()
    2. .permission(permissions)
    3. .rationale(new RuntimeRationale())
    4. .onGranted(new Action<List<String>>() {
    5. @Override
    6. public void onAction(List<String> permissions) {
    7. toast(R.string.successfully);
    8. }
    9. })
    10. .onDenied(new Action<List<String>>() {
    11. @Override
    12. public void onAction(@NonNull List<String> permissions) {
    13. toast(R.string.failure);
    14. if (AndPermission.hasAlwaysDeniedPermission(MainActivity.this, permissions)) {
    15. showSettingDialog(MainActivity.this, permissions);
    16. }
    17. }
    18. })
    19. .start();

    复制代码

  • 3.安装应用

    private void installPackage() {

    1. AndPermission.with(this)
    2. .install()
    3. .file(new File(Environment.getExternalStorageDirectory(), "android.apk"))
    4. .rationale(new InstallRationale())
    5. .onGranted(new Action<File>() {
    6. @Override
    7. public void onAction(File data) {
    8. // Installing.
    9. toast(R.string.successfully);
    10. }
    11. })
    12. .onDenied(new Action<File>() {
    13. @Override
    14. public void onAction(File data) {
    15. // The user refused to install.
    16. toast(R.string.failure);
    17. }
    18. })
    19. .start();
    20. }

    复制代码

  • 4.应用上显示(悬浮窗)

    private void requestPermissionForAlertWindow() {

    1. AndPermission.with(this).overlay().rationale(new OverlayRationale()).onGranted(new Action<Void>() {
    2. @Override
    3. public void onAction(Void data) {
    4. toast(R.string.successfully);
    5. showAlertWindow();
    6. }
    7. }).onDenied(new Action<Void>() {
    8. @Override
    9. public void onAction(Void data) {
    10. toast(R.string.failure);
    11. }
    12. }).start();
    13. }

    复制代码

  • 5.修改系统设置

    private void requestWriteSystemSetting() {

    1. AndPermission.with(this).setting().write().rationale(new WriteSettingRationale()).onGranted(new Action<Void>() {
    2. @Override
    3. public void onAction(Void data) {
    4. toast(R.string.successfully);
    5. }
    6. }).onDenied(new Action<Void>() {
    7. @Override
    8. public void onAction(Void data) {
    9. toast(R.string.failure);
    10. }
    11. }).start();
    12. }

    复制代码

  • 6.通知

    private void requestNotificationListener() {

    1. AndPermission.with(this)
    2. .notification()
    3. .listener()
    4. .rationale(new NotifyListenerRationale())
    5. .onGranted(new Action<Void>() {
    6. @Override
    7. public void onAction(Void data) {
    8. toast(R.string.successfully);
    9. }
    10. })
    11. .onDenied(new Action<Void>() {
    12. @Override
    13. public void onAction(Void data) {
    14. toast(R.string.failure);
    15. }
    16. })
    17. .start();
    18. }

    复制代码

源码分析

  • 1.with方法

    public static Option with(Context context) {

    1. return new Boot(getContextSource(context));
    2. }

    public static Option with(Fragment fragment) {

    1. return new Boot(new SupportFragmentSource(fragment));
    2. }

    public static Option with(android.app.Fragment fragment) {

    1. return new Boot(new FragmentSource(fragment));
    2. }

    public static Option with(Activity activity) {

    1. return new Boot(new ActivitySource(activity));
    2. }

    复制代码

with方法可以传入ContextFragmentandroid.app.Fragment fragmentActivity,返回的都是Option对象,而它是个接口,我们来看看Option的实现类Boot

  1. public class Boot implements Option {
  2. public Boot(Source source) {
  3. this.mSource = source;
  4. }
  5. @Override
  6. public RuntimeOption runtime() {
  7. return new Runtime(mSource);
  8. }
  9. @Override
  10. public InstallRequest install() {
  11. return INSTALL_REQUEST_FACTORY.create(mSource);
  12. }
  13. @Override
  14. public OverlayRequest overlay() {
  15. return OVERLAY_REQUEST_FACTORY.create(mSource);
  16. }
  17. @Override
  18. public NotifyOption notification() {
  19. return new Notify(mSource);
  20. }
  21. @Override
  22. public Setting setting() {
  23. return new Setting(mSource);
  24. }
  25. }
  26. 复制代码

可以看到,这里对不同的Request做了封装,对以后的扩展非常有利,这也是AndPermission的亮点之一。

之前传入的ContextFragmentandroid.app.Fragment fragmentActivity只影响的startActivitystartActivityForResultisShowRationalePermission方法

Source相关代码

  1. public abstract void startActivity(Intent intent);
  2. public abstract void startActivityForResult(Intent intent, int requestCode);
  3. public abstract boolean isShowRationalePermission(String permission);
  4. 复制代码

ActivitySource相关代码

  1. public class ActivitySource extends Source {
  2. @Override
  3. public void startActivity(Intent intent) {
  4. mActivity.startActivity(intent);
  5. }
  6. @Override
  7. public void startActivityForResult(Intent intent, int requestCode) {
  8. mActivity.startActivityForResult(intent, requestCode);
  9. }
  10. @Override
  11. public boolean isShowRationalePermission(String permission) {
  12. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false;
  13. return mActivity.shouldShowRequestPermissionRationale(permission);
  14. }
  15. }
  16. 复制代码
  • 2.permission

    @Override

    1. public PermissionRequest permission(@NonNull String... permissions) {
    2. //是否在`manifest.xml`中注册
    3. checkPermissions(permissions);
    4. return FACTORY.create(mSource).permission(permissions);
    5. }

    复制代码

这里首先对传入的权限做了检查,是否在manifest.xml中注册,然后调用FACTORY.create创建了一个PermissionRequest

  1. static {
  2. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  3. FACTORY = new MRequestFactory();
  4. } else {
  5. FACTORY = new LRequestFactory();
  6. }
  7. }
  8. public interface PermissionRequestFactory {
  9. PermissionRequest create(Source source);
  10. }
  11. 复制代码
  • 3.rationale相当于是个拦截器,当没有权限时会执行。

这个有什么用呢?比如说,App需要申请全局悬浮窗权限,相比直接跳到授权页,弹个提示框由用户选择是否去授权就显得友好的多。 当showRationale()被回调后说明没有权限,此时开发者必须回调RequestExecutor#execute()来启动设置或者RequestExecutor#cancel()来取消启动设置,否则将不会回调onGranted()或者onDenied()中的任何一个,也就是说AndPermission将不会有任何响应。

  • 4.onGranted同意授权时调用,onDenied拒绝授权时调用
  • 5.start开始授权

MRequest相关代码如下

  1. @Override
  2. public void start() {
  3. List<String> deniedList = getDeniedPermissions(STANDARD_CHECKER, mSource, mPermissions);
  4. mDeniedPermissions = deniedList.toArray(new String[deniedList.size()]);
  5. if (mDeniedPermissions.length > 0) {
  6. List<String> rationaleList = getRationalePermissions(mSource, mDeniedPermissions);
  7. if (rationaleList.size() > 0) {
  8. mRationale.showRationale(mSource.getContext(), rationaleList, this);
  9. } else {
  10. execute();
  11. }
  12. } else {
  13. onCallback();
  14. }
  15. }
  16. 复制代码

首先,判断传递进来的权限有哪些是没有已授权,如都已授权,直接回调成功;如有未授权的,先判断是否有需要拦截的,如没有,则调用execute方法

  1. public void execute() {
  2. BridgeRequest request = new BridgeRequest(mSource);
  3. request.setType(BridgeRequest.TYPE_PERMISSION);
  4. request.setPermissions(mDeniedPermissions);
  5. request.setCallback(this);
  6. RequestManager.get().add(request);
  7. }
  8. 复制代码

RequestManager 它的核心是个线程池

相关方法

  1. private RequestManager() {
  2. this.mQueue = new LinkedBlockingQueue<>();
  3. new RequestExecutor(mQueue).start();
  4. }
  5. public void add(BridgeRequest request) {
  6. mQueue.add(request);
  7. }
  8. 复制代码

RequestExecutor相关方法

  1. public void run() {
  2. while (true) {
  3. synchronized (this) {
  4. try {
  5. mRequest = mQueue.take();
  6. } catch (InterruptedException e) {
  7. continue;
  8. }
  9. mMessenger = new Messenger(mRequest.getSource().getContext(), this);
  10. mMessenger.register();
  11. executeCurrent();
  12. try {
  13. wait();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }
  19. }
  20. private void executeCurrent() {
  21. ...省略...
  22. case BridgeRequest.TYPE_PERMISSION:
  23. BridgeActivity.requestPermission(mRequest.getSource(), mRequest.getPermissions());
  24. break;
  25. ...省略...
  26. }
  27. 复制代码

再来看看 BridgeActivity.requestPermission

  1. static void requestPermission(Source source, String[] permissions) {
  2. Intent intent = new Intent(source.getContext(), BridgeActivity.class);
  3. intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_PERMISSION);
  4. intent.putExtra(KEY_PERMISSIONS, permissions);
  5. source.startActivity(intent);
  6. }
  7. 复制代码

BridgeActivity相关代码

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. ...省略...
  4. String[] permissions = intent.getStringArrayExtra(KEY_PERMISSIONS);
  5. requestPermissions(permissions, BridgeRequest.TYPE_PERMISSION);
  6. ...省略...
  7. }
  8. 复制代码

看到这里大家就比较熟悉了,最后我们在看看授权的回调处理

  1. @Override
  2. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  3. Messenger.send(this);
  4. finish();
  5. }
  6. 复制代码

Messenger相关代码

  1. public static void send(Context context) {
  2. Intent broadcast = new Intent(ACTION);
  3. context.sendBroadcast(broadcast);
  4. }
  5. @Override
  6. public void onReceive(Context context, Intent intent) {
  7. mCallback.onCallback();
  8. }
  9. 复制代码

这里回调到了RequestExecutor#onCallback方法,而其又回调到了MRequest#onCallback方法

MRequest相关代码

  1. @Override
  2. public void onCallback() {
  3. new AsyncTask<Void, Void, List<String>>() {
  4. @Override
  5. protected List<String> doInBackground(Void... voids) {
  6. return getDeniedPermissions(DOUBLE_CHECKER, mSource, mPermissions);
  7. }
  8. @Override
  9. protected void onPostExecute(List<String> deniedList) {
  10. if (deniedList.isEmpty()) {
  11. callbackSucceed();
  12. } else {
  13. callbackFailed(deniedList);
  14. }
  15. }
  16. }.execute();
  17. }
  18. 复制代码

这里再次对传入的权限做检查,如果没有未授权,则回调成功,否则回调失败。

  • 6.用户拒绝授权时提示

    if (AndPermission.hasAlwaysDeniedPermission(MainActivity.this, permissions)) {

    1. showSettingDialog(MainActivity.this, permissions);
    2. }

    复制代码

最终调用的逻辑是在SettingPage#start方法,这里对不同的机型做了适配

  1. public void start(int requestCode) {
  2. Intent intent;
  3. if (MARK.contains("huawei")) {
  4. intent = huaweiApi(mSource.getContext());
  5. } else if (MARK.contains("xiaomi")) {
  6. intent = xiaomiApi(mSource.getContext());
  7. } else if (MARK.contains("oppo")) {
  8. intent = oppoApi(mSource.getContext());
  9. } else if (MARK.contains("vivo")) {
  10. intent = vivoApi(mSource.getContext());
  11. } else if (MARK.contains("meizu")) {
  12. intent = meizuApi(mSource.getContext());
  13. } else {
  14. intent = defaultApi(mSource.getContext());
  15. }
  16. try {
  17. mSource.startActivityForResult(intent, requestCode);
  18. } catch (Exception e) {
  19. intent = defaultApi(mSource.getContext());
  20. mSource.startActivityForResult(intent, requestCode);
  21. }
  22. }
  23. 复制代码

总结

设计思想:

  • 1.构建请求Request
  • 2.封装参数
  • 3.添加到线程池
  • 4.执行完毕,回调

这和Glide是惊人的相似,详情可以查看【源码阅读】Glide源码阅读之with方法(一)系列文章

结束语

这里只分析了runtime的设计思想,installoverlaynotification与其类似,就不一一分析了。

转载于:https://juejin.im/post/5cd38f7de51d45368a619a7b

发表评论

表情:
评论列表 (有 0 条评论,477人围观)

还没有评论,来说两句吧...

相关阅读

    相关 tinyhttp阅读

    1.综述 这是一个小型的开源http服务,总代码量加上注释一共482行,实际上真正的核心代码就350行左右。虽然代码量不多,但是一款http服务器最基本的通信业务流程都具