美文网首页菜菜Android学习Android开发技术分享
讲讲郭大的运行时权限的封装姿势

讲讲郭大的运行时权限的封装姿势

作者: 呆呆IT人 | 来源:发表于2017-05-09 10:03 被阅读291次

    一.WHAT

    Android 6.0在我们原有的AndroidManifest.xml声明权限的基础上,又新增了运行时权限动态检测,以下权限都需要在运行时判断:

     身体传感器、日历、摄像头、通讯录、地理位置、麦克风、电话、短信、存储空间
    

    二.HOW

    ----笨笨的方式

    Android6.0系统默认为targetSdkVersion小于23的应用默认授予了所申请的所有权限,所以如果你以前的APP设置的targetSdkVersion低于23,在运行时也不会崩溃,但这也只是一个临时的救急策略,用户还是可以在设置中取消授予的权限。

    声明目标SDK版本

    我们需要在build.gradle中声明targetSdkVersion为23

    android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"
    defaultConfig {   
    applicationId "com.yourcomany.app   
    minSdkVersion 18   
    targetSdkVersion 23   ...........................
    

    我们需要在用到权限的地方,每次都检查是否APP已经拥有权限,比如我们有一个下载功能,需要写SD卡的权限,我们在写入之前检查是否有WRITE_EXTERNAL_STORAGE权限,没有则申请权限

    if(ContextCompat.checkSelfPermission(this,Manifest.permission.
    WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {
    
    //申请WRITE_EXTERNAL_STORAGE权限  ActivityCompat.requestPermissions(this,newString[{Manifest
    .permission.WRITE_EXTERNAL_STORAGE},WRITE_EXTERNAL_STORAGE_REQUEST_CODE);}
    

    请求权限后,系统会弹出请求权限的Dialog

    用户选择允许或需要后,会回调onRequestPermissionsResult方法, 该方法类似于onActivityResult

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    doNext(requestCode,grantResults);
    }
    

    我们接着需要根据requestCode和grantResults(授权结果)做相应的后续处理

    private void doNext(int requestCode, int[] grantResults) {
    if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    // Permission Granted
    } else {
    // Permission Denied
    }}}
    

    Fragment中运行时权限的特殊处理
    在Fragment中申请权限,不要使用ActivityCompat.requestPermissions, 直接使用Fragment的requestPermissions方法,否则会回调到Activity的 onRequestPermissionsResult

    如果在Fragment中嵌套Fragment,在子Fragment中使用requestPermissions方 法,onRequestPermissionsResult不会回调回来,建议使用 getParentFragment().requestPermissions方法,这个方法会回调到父Fragment中的onRequestPermissionsResult,加入以下代码可以把回调透传到子Fragment

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    List fragments = getChildFragmentManager().getFragments();
    if (fragments != null) {
    for (Fragment fragment : fragments) {
    if (fragment != null) {
    fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);}}}}
    

    上面这种方式,那么我们每次都需要在使用此权限的页面重复上面的代码!

    平时最最不能容忍的就是重复的代码,

    我想大家现在肯定有了一点思路,傻呀,直接写到BaseActivity里。

    ------恩,一个不错的方式

    首先呢,我们应该新建一个权限申请的监听回调接口,就叫他RequestPermisListener吧
    它呢主要是处理申请权限时,对于申请的结果的回调
    我们来看看它长神马样子:
    public interface RequestPermisListener {
    //全部成功
    void onGranted();
    //拒绝的权限,集合是为了回调的时候,能够清楚拒绝了哪些权限,并作出相应的处理
    void onDenide(List<String> permissions);
    }
    
    嗯短小精干,每次申请权限时实现它就行了
    现在我们来看看BaseActivity的样子
    public abstract class BaseActivity{
    private final String TAG="BaseActivity";
    private static RequestPermisListener mListener;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(getLayoutId());
    ButterKnife.bind(this);}
    /**
    * 权限的申请
    *
    * @param permis
    * @param listener
    */
    public void requestPermission(String permis[],RequestPermisListener listener) {
    mListener=listener;
    //装我们要处理申请的权限
    List<String> permissions=new ArrayList<>();
    //开启循环,遍历传入的权限是不是已经被用户同意了,
    for (int i = 0; i < permis.length; i++) {
            if (ContextCompat.checkSelfPermission(this, permis[i]) != PackageManager.PERMISSION_GRANTED) {
                //将没有同意过的权限添加到申请的集合中去
                permissions.add(permis[i]);
            }
        }
        //集合不为空,则表示有没有同意的权限
        if (!permissions.isEmpty()) {
            //我们需要处理申请
            ActivityCompat.requestPermissions(activity, permissions.toArray(new String[permissions.size()]), 1);
        } else {
            //为空则表示申请的权限全部已经同意过,
            listener.onGranted();
        }
        }
    /**
    * 权限申请结果的处理
    *
    * @param requestCode
    * @param permissions
    * @param grantResults
    */
    @Override
    public void onRequestPermissionsResult(int requestCode,@NonNull String[]permissions,@NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch(requestCode) {
      case 1:
         if (grantResults.length > 0) {
                    List<String> denidePermission = new ArrayList<>();
                    for (int i = 0; i < grantResults.length; i++) {
                        int grantResult = grantResults[i];
                        String permission = permissions[I];
                        //将用户拒绝的权限,添加到拒绝的集合中去
                         if (grantResult == PackageManager.PERMISSION_DENIED) {
                            denidePermission.add(permission);
                        }
                    }
                    //拒绝的集合为空,自然全部同意,回调申请成功
                    if (denidePermission.isEmpty()) {
                        mListener.onGranted();
                    } else {
                        //否则,回调失败的接口,并将拒绝的权限传回,
                        mListener.onDenide(denidePermission);
                    }
                }
    
                break;
        }
    }
    
    /**
    * @return 返回视图的布局id
    */
    protected abstract int getLayoutId();
    
    }
    

    一个项目往往都会有一个Activity的基类,我们将权限请求的逻辑,统统放在了里面,每次申请权限时,只需要将要请求的权限装在一个String数组里,这样满足了大多数的场景,那么问题来了,假如我们想要在其他情况下申请呢,大家注意到面申请那里吗,首先我们需要Activity的引用,还有就是回调是发生在Activity中,
    那么就引发了下面的一个方法。。

    -----这也许是更好的方式

    首先我们需要一个Activity的管理类,就叫他ActivityManager吧

    /**
     * Created by Zcoder
     * Email : 1340751953@qq.com
     * Time :  2017/5/2
     * Description : Activity的管理类
     */
    
    public class ActivityManager {
        private static List<Activity> activitys = new ArrayList<>();
       //当Activity创建的时候,我们将其添加
        public static void addActivity(Activity activity) {
            activitys.add(activity);
        }
         //当Activity销毁的时候,我们将其移除
        public static void removeActivity(Activity activity) {
            activitys.remove(activity);
        }
        //获得目前处于顶端的Activity
        public static Activity getTopActivitys() {
            if (!activitys.isEmpty()) {
                return activitys.get(activitys.size() - 1);
            } else {
                return null;
            }
        }
    
        public static List<Activity> getActivitys(){
            return activitys;
        }
    }
    

    BaseActivity的修改,将权限请求方法,静态并Public,其中的Activity是从我们的ActivityManager中拿到当前处于顶部的Activity

    //创建的时候将其添加到ActivityManager中
     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityManager.addActivity(this);
        setContentView(getLayoutId());
        ButterKnife.bind(this);
    
        }
      //注意看。。。。。。
      public static void requestPermission(String permis[], RequestPermisListener listener) {
            mListener = listener;
            List<String> permissions = new ArrayList<>();
            //注意*********************** Activity并不是当前的this
            Activity activity = ActivityManager.getTopActivitys();
            for (int i = 0; i < permis.length; i++) {
                if (ContextCompat.checkSelfPermission(activity, permis[i]) !=     PackageManager.PERMISSION_GRANTED) {
                    permissions.add(permis[i]);
                }
            }
            if (!permissions.isEmpty()) {
                ActivityCompat.requestPermissions(activity, permissions.toArray(new String[permissions.size()]),     1);
            } else {
                listener.onGranted();
            }
        }
    
    //销毁的时候将其从 ActivityManager移除
      @Override
        protected void onDestroy() {
            super.onDestroy();
            ActivityManager.removeActivity(this);
        }
    }
    
    我的一段权限申请

    我的一个App启动界面每次服务器壁纸与本地的不一致时,就需要下载服务器端的壁纸,位置时存在外部储存
    所以没次进行下载都需要判断下权限,不然6.0会写入失败
    如果你感兴趣可以去看看
    养眼.一个看妹子图片的开源App(MVP+Retrofit+Rxjava)
    地址:http://www.jianshu.com/p/3d744d4dc726

     //下载新的壁纸
    private void downloadNewImage(final String path, final String url) {
        Logger.e("开始下载新的图片");
        requestPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, new RequestPermisListener() {
            @Override
            public void onGranted() {
                RxVolley.download(path, url, null, new HttpCallback() {
                    @Override
                    public void onSuccess(String t) {
                        super.onSuccess(t);
                        Logger.e("下载成功");
                    }
                });
            }
    
            @Override
            public void onDenide(List<String> permissions) {
                ToastUtils.showLongToast(getString(R.string.permission_error_sd));
            }
        });
    
    }
    

    三.WHY

    我认为郭大的这种方法十分的不错,而且也是很容易理解的
    源码地址:
    BaseActivity.java
    https://github.com/miaoMiaoDaShi/Yangyan/blob/master/app/src/main/java/com/xxp/yangyan/pro/base/BaseActivity.java
    ActivityManager.java
    https://github.com/miaoMiaoDaShi/Yangyan/blob/master/app/src/main/java/com/xxp/yangyan/pro/utils/ActivityManager.java

    四.LAST

    最后要说,不要忘了在清单文件里说明!!!!切记!

    最后要说,不要忘了在清单文件里说明!!!!切记!

    最后要说,不要忘了在清单文件里说明!!!!切记!

    相关文章

      网友评论

      • 4640f78109a4:老铁,文章很好啊!就是有错别字,是回调入口,不是毁掉入口:grin:
        呆呆IT人: @fmlong 哈哈,谢谢提醒!

      本文标题:讲讲郭大的运行时权限的封装姿势

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