美文网首页
【源码阅读】AndPermission源码阅读

【源码阅读】AndPermission源码阅读

作者: 欢子3824 | 来源:发表于2019-05-09 10:23 被阅读0次

    前言

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

    AndPermission

    思路

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


    image.png

    使用

    • 1.添加引用
      implementation 'com.yanzhenjie.permission:support:2.0.1'
    • 2.请求权限
            AndPermission.with(this)
                .runtime()
                .permission(permissions)
                .rationale(new RuntimeRationale())
                .onGranted(new Action<List<String>>() {
                    @Override
                    public void onAction(List<String> permissions) {
                        toast(R.string.successfully);
                    }
                })
                .onDenied(new Action<List<String>>() {
                    @Override
                    public void onAction(@NonNull List<String> permissions) {
                        toast(R.string.failure);
                        if (AndPermission.hasAlwaysDeniedPermission(MainActivity.this, permissions)) {
                            showSettingDialog(MainActivity.this, permissions);
                        }
                    }
                })
                .start();
    
    • 3.安装应用
        private void installPackage() {
            AndPermission.with(this)
                .install()
                .file(new File(Environment.getExternalStorageDirectory(), "android.apk"))
                .rationale(new InstallRationale())
                .onGranted(new Action<File>() {
                    @Override
                    public void onAction(File data) {
                        // Installing.
                        toast(R.string.successfully);
                    }
                })
                .onDenied(new Action<File>() {
                    @Override
                    public void onAction(File data) {
                        // The user refused to install.
                        toast(R.string.failure);
                    }
                })
                .start();
        }
    
    • 4.应用上显示(悬浮窗)
        private void requestPermissionForAlertWindow() {
            AndPermission.with(this).overlay().rationale(new OverlayRationale()).onGranted(new Action<Void>() {
                @Override
                public void onAction(Void data) {
                    toast(R.string.successfully);
                    showAlertWindow();
                }
            }).onDenied(new Action<Void>() {
                @Override
                public void onAction(Void data) {
                    toast(R.string.failure);
                }
            }).start();
        }
    
    • 5.修改系统设置
        private void requestWriteSystemSetting() {
            AndPermission.with(this).setting().write().rationale(new WriteSettingRationale()).onGranted(new Action<Void>() {
                @Override
                public void onAction(Void data) {
                    toast(R.string.successfully);
                }
            }).onDenied(new Action<Void>() {
                @Override
                public void onAction(Void data) {
                    toast(R.string.failure);
                }
            }).start();
        }
    
    • 6.通知
        private void requestNotificationListener() {
            AndPermission.with(this)
                .notification()
                .listener()
                .rationale(new NotifyListenerRationale())
                .onGranted(new Action<Void>() {
                    @Override
                    public void onAction(Void data) {
                        toast(R.string.successfully);
                    }
                })
                .onDenied(new Action<Void>() {
                    @Override
                    public void onAction(Void data) {
                        toast(R.string.failure);
                    }
                })
                .start();
        }
    

    源码分析

    • 1.with方法
    public static Option with(Context context) {
            return new Boot(getContextSource(context));
        }
    public static Option with(Fragment fragment) {
            return new Boot(new SupportFragmentSource(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));
        }
    

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

    public class Boot implements Option {
        public Boot(Source source) {
            this.mSource = source;
        }
        @Override
        public RuntimeOption runtime() {
            return new Runtime(mSource);
        }
        @Override
        public InstallRequest install() {
            return INSTALL_REQUEST_FACTORY.create(mSource);
        }
        @Override
        public OverlayRequest overlay() {
            return OVERLAY_REQUEST_FACTORY.create(mSource);
        }
        @Override
        public NotifyOption notification() {
            return new Notify(mSource);
        }
        @Override
        public Setting setting() {
            return new Setting(mSource);
        }
    }
    

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

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

    Source相关代码

        public abstract void startActivity(Intent intent);
    
        public abstract void startActivityForResult(Intent intent, int requestCode);
    
        public abstract boolean isShowRationalePermission(String permission);
    

    ActivitySource相关代码

    public class ActivitySource extends Source {
    
        @Override
        public void startActivity(Intent intent) {
            mActivity.startActivity(intent);
        }
        @Override
        public void startActivityForResult(Intent intent, int requestCode) {
            mActivity.startActivityForResult(intent, requestCode);
        }
        @Override
        public boolean isShowRationalePermission(String permission) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false;
            return mActivity.shouldShowRequestPermissionRationale(permission);
        }
    }
    
    • 2.permission
        @Override
        public PermissionRequest permission(@NonNull String... permissions) {
            //是否在`manifest.xml`中注册
            checkPermissions(permissions);
            return FACTORY.create(mSource).permission(permissions);
        }
    

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

        static {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                FACTORY = new MRequestFactory();
            } else {
                FACTORY = new LRequestFactory();
            }
        }
        public interface PermissionRequestFactory {
            PermissionRequest create(Source source);
        }
    
    • 3.rationale相当于是个拦截器,当没有权限时会执行。

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

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

    MRequest相关代码如下

        @Override
        public void start() {
            List<String> deniedList = getDeniedPermissions(STANDARD_CHECKER, mSource, mPermissions);
            mDeniedPermissions = deniedList.toArray(new String[deniedList.size()]);
            if (mDeniedPermissions.length > 0) {
                List<String> rationaleList = getRationalePermissions(mSource, mDeniedPermissions);
                if (rationaleList.size() > 0) {
                    mRationale.showRationale(mSource.getContext(), rationaleList, this);
                } else {
                    execute();
                }
            } else {
                onCallback();
            }
        }
    

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

        public void execute() {
            BridgeRequest request = new BridgeRequest(mSource);
            request.setType(BridgeRequest.TYPE_PERMISSION);
            request.setPermissions(mDeniedPermissions);
            request.setCallback(this);
            RequestManager.get().add(request);
        }
    

    RequestManager 它的核心是个线程池

    相关方法

        private RequestManager() {
            this.mQueue = new LinkedBlockingQueue<>();
    
            new RequestExecutor(mQueue).start();
        }
        public void add(BridgeRequest request) {
            mQueue.add(request);
        }
    

    RequestExecutor相关方法

        public void run() {
            while (true) {
                synchronized (this) {
                    try {
                        mRequest = mQueue.take();
                    } catch (InterruptedException e) {
                        continue;
                    }
                    mMessenger = new Messenger(mRequest.getSource().getContext(), this);
                    mMessenger.register();
                    executeCurrent();
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        private void executeCurrent() {
                ...省略...
                case BridgeRequest.TYPE_PERMISSION: 
                    BridgeActivity.requestPermission(mRequest.getSource(), mRequest.getPermissions());
                    break;
                ...省略...
        }
    

    再来看看 BridgeActivity.requestPermission

        static void requestPermission(Source source, String[] permissions) {
            Intent intent = new Intent(source.getContext(), BridgeActivity.class);
            intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_PERMISSION);
            intent.putExtra(KEY_PERMISSIONS, permissions);
            source.startActivity(intent);
        }
    

    BridgeActivity相关代码

        @Override
        protected void onCreate(Bundle savedInstanceState) {
                     ...省略...
                     String[] permissions = intent.getStringArrayExtra(KEY_PERMISSIONS);
                    requestPermissions(permissions, BridgeRequest.TYPE_PERMISSION);
                     ...省略...
         }
    

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

        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
            Messenger.send(this);
            finish();
        }
    

    Messenger相关代码

        public static void send(Context context) {
            Intent broadcast = new Intent(ACTION);
            context.sendBroadcast(broadcast);
        }
        @Override
        public void onReceive(Context context, Intent intent) {
            mCallback.onCallback();
        }
    
    

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

    MRequest相关代码

        @Override
        public void onCallback() {
            new AsyncTask<Void, Void, List<String>>() {
                @Override
                protected List<String> doInBackground(Void... voids) {
                    return getDeniedPermissions(DOUBLE_CHECKER, mSource, mPermissions);
                }
                @Override
                protected void onPostExecute(List<String> deniedList) {
                    if (deniedList.isEmpty()) {
                        callbackSucceed();
                    } else {
                        callbackFailed(deniedList);
                    }
                }
            }.execute();
        }
    

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

    • 6.用户拒绝授权时提示
                        if (AndPermission.hasAlwaysDeniedPermission(MainActivity.this, permissions)) {
                            showSettingDialog(MainActivity.this, permissions);
                        }
    

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

        public void start(int requestCode) {
            Intent intent;
            if (MARK.contains("huawei")) {
                intent = huaweiApi(mSource.getContext());
            } else if (MARK.contains("xiaomi")) {
                intent = xiaomiApi(mSource.getContext());
            } else if (MARK.contains("oppo")) {
                intent = oppoApi(mSource.getContext());
            } else if (MARK.contains("vivo")) {
                intent = vivoApi(mSource.getContext());
            } else if (MARK.contains("meizu")) {
                intent = meizuApi(mSource.getContext());
            } else {
                intent = defaultApi(mSource.getContext());
            }
            try {
                mSource.startActivityForResult(intent, requestCode);
            } catch (Exception e) {
                intent = defaultApi(mSource.getContext());
                mSource.startActivityForResult(intent, requestCode);
            }
        }
    

    总结

    设计思想:

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

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

    结束语

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

    相关文章

      网友评论

          本文标题:【源码阅读】AndPermission源码阅读

          本文链接:https://www.haomeiwen.com/subject/imjvoqtx.html