菜菜Android学习Android开发技术分享

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

2017-05-09  本文已影响291人  呆呆IT人

一.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

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

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

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

上一篇下一篇

猜你喜欢

热点阅读