1.4 Android6.0权限管理-开源框架源码分析

1.4 Android6.0权限管理-开源框架源码分析

引言

Android6.0这个系统中迎来的最大的特征莫过于就是它的权限管理,因为小猪在写教程的时候应该6.0还没出,但是我感觉随着Android6.0的普及,对于它的权限管理是开发中必备的技能,同时也是基础。

乱入一些话

对于这种开发中所必备的技能,已经系统对此的简化操作,很多博主已经写了类似的博客。这里感觉我再废话一篇文章依旧是废话。

但是同样是基础,同样大家面临着Android6.0的权限管理,不同人对此的解决方案会有不同,所以这篇文章不妨带大家分析几个github上的开源框架上是如何解决(封装)这个权限管理的。


PermissionGen

在鸿神一篇中提到一篇相对基础的权限管理框架,那么接下来我们就围绕着这个框架去探索,学习别人是如何解决权限管理问题的。

github传送门

使用方式

1. 使用下面的一种方式去获取需要的权限

PermissionGen.with(MainActivity.this)
.
addRequestCode(100)
.
permissions(
Manifest.permission.READ_CONTACTS,
Manifest.permission.RECEIVE_SMS,
Manifest.permission.WRITE_CONTACTS)
.
request();

or

PermissionGen.needPermission(ContactFragment.this, 100,
new String[] {
Manifest.permission.READ_CONTACTS,
Manifest.permission.RECEIVE_SMS,
Manifest.permission.WRITE_CONTACTS
}
);

2.重写 onRequestPermissionsResult 方法

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

3.回调

正确:

@PermissionSuccess(requestCode = 100)
public void doSomething(){
Toast.makeText(this, Contact permission is granted, Toast.LENGTH_SHORT).show();
}

错误:

@PermissionFail(requestCode = 100)
public void doFailSomething(){
Toast.makeText(this, Contact permission is not granted, t.LENGTH_SHORT).show();
}

往往一个框架,简单的使用方式,背后大多数是非常复杂的架构或者设计模式,这个架构的代码量以及难度不是非常大,配合源码食用更佳。

基本分析

成员变量

//保存需要的权限
private String[] mPermissions;
//请求码
private int mRequestCode;
//保存Activity or Fragment
private Object object;

构造方法

//私有化了构造方法
private PermissionGen(Object object) {
this.object = object;
}

with方法

很多开源库惯用的手法,既传入Activity or Fragment的方法,并且调用构造方法,我想这个大家应该不能理解。

public static PermissionGen with(Activity activity){
return new PermissionGen(activity);
}

public static PermissionGen with(Fragment fragment){
return new PermissionGen(fragment);
}

赋值

既然有了对象,接下来就是对对象的成员变量进行赋值了。

//当然还有另外一种方式去添加 permissions 就是 needPermission 留个大家自己去思考咯
public PermissionGen permissions(Stringpermissions){
this.mPermissions = permissions;
return this;
}

public PermissionGen addRequestCode(int requestCode){
this.mRequestCode = requestCode;
return this;
}

上述几个流程是非常简单的,如果你经常拆轮子,对于这些手法已经见怪不怪了,接下来就说说他是如果通过注解反射来进行权限申请已经回调的吧。

核心分析

requestResult

在使用方式的第二步调用了 PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults); ,这是一个重载的方法,主要是第一个参数会不同,this可以是指向的对象可以是Fragment or Activity,但是最终都回调到一个方法。

public static void onRequestPermissionsResult(Activity activity, int requestCode, String[] permissions,int[] grantResults) {
requestResult(activity, requestCode, permissions, grantResults);
}

public static void onRequestPermissionsResult(Fragment fragment, int requestCode, String[] permissions,int[] grantResults) {
requestResult(fragment, requestCode, permissions, grantResults);
}

最终都调用以下方法:

private static void requestResult(Object obj, int requestCode, String[] permissions,int[] grantResults){
ListString> deniedPermissions = new ArrayList();
for(int i=0; igrantResults.length; i++){
//每一天通过的权限
if(grantResults[i] != PackageManager.PERMISSION_GRANTED){
deniedPermissions.add(permissions[i]);
}
}
//如果有没通过的权限就失败
if(deniedPermissions.size() > 0){
doExecuteFail(obj, requestCode);
} else {
doExecuteSuccess(obj, requestCode);
}
}

使用注解的方式回调

正确时的回调的放阿飞

private static void doExecuteFail(Object activity, int requestCode) {
//方法一
Method executeMethod = Utils.findMethodWithRequestCode(activity.getClass(),PermissionFail.class, requestCode);
//方法二
executeMethod(activity, executeMethod);
}

方法一:

public static A extends Annotation> Method findMethodWithRequestCode(Class clazz,ClassA> annotation, int requestCode) {
//循环这个对象中的方法
for(Method method : clazz.getDeclaredMethods()){
//判断每个方法的注解是不是符合 annotation(PermissionSuccess or PermissionFail)
if(method.isAnnotationPresent(annotation)){
//最后调用了这个方法,就不copy了,非常简单,判断他的 requestCode
if(isEqualRequestCodeFromAnntation(method, annotation, requestCode)){
return method;
}
}
}
return null;
}

方法二:

通过反射我们拿到了需要的方法,接下来就是运行这个方法了

private static void executeMethod(Object activity, Method executeMethod) {
if(executeMethod != null){
try {
if(!executeMethod.isAccessible()) executeMethod.setAccessible(true);
//运行这个方法
executeMethod.invoke(activity, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}

是不是非常简单,简单明了的几步流程,然后我们再睡觉描一眼 PermisstionSuccessPermisstionFail 两个类

@Target(ElementType.METHOD)
@
Retention(RetentionPolicy.RUNTIME)
public @interface PermissionFail {
int requestCode();
}

@Retention(RetentionPolicy.RUNTIME)
@
Target(ElementType.METHOD)
public @interface PermissionSuccess {
int requestCode();
}

无非就是两个运行注解,参数为 int 类型。到这里 PermisstionGen框架解说完毕,接下来我们再来聊聊鸿神的框架。 github传送门


MPermissions

思路无非大同小异,最大的后者使用了编译注解,这个特点非常多的开源框架会选择使用,基于这点,就不存在反射损失效率的问题了。

使用方式

所有 Permisstion 相关的框架使用方式都是相同的,无非就是需要的权限,onRequestPermissionsResult 中去调用静态方法,最后回调,所以这里就不copy别人的代码来凑字数了,我们直接切入主题吧。

分析

依赖
因为使用了编译时注解,所以就会有依赖关系。

permission-sample 依赖 permission-api(也就是lib)
permission-lib 依赖 permission-annotation
compile 依赖 permission-annotation

注解类

permission-annotation 下有三个注解类

  • 授权成功 PermissionGrant

    @Target(ElementType.METHOD)
    public @interface PermissionGrant{
    int value();
    }
  • 授权失败 PermissionDenied

    @Target(ElementType.METHOD)
    public @interface PermissionDenied {
    int value();
    }
  • 如果拒接了就提醒

    @Target(ElementType.METHOD)
    @
    Retention(RetentionPolicy.CLASS)
    public @interface ShowRequestPermissionRationale {
    int value();
    }

完成了上个注解处理器,接下来就是给 permission-lib 添加依赖,添加依赖后,我们来看看 permission-lib 中的主类。 【注:考虑到compile没做解说,因为有点复杂,后面我后另开一篇文章详细说明编译注解】

requestPermissions

和上一个框架非常类似的方法,但是直接抛弃了链式编程,直接把3个参数一坨放进去了。

public static void requestPermissions(Activity object, int requestCode, Stringpermissions){
_requestPermissions(object, requestCode, permissions);
}

public static void requestPermissions(Fragment object, int requestCode, Stringpermissions){
_requestPermissions(object, requestCode, permissions);
}

同样属于重载,来区分Activity与Fragment,最后调用下面的方法。

private static void _requestPermissions(Object object, int requestCode, Stringpermissions){
if (!Utils.isOverMarshmallow()){
doExecuteSuccess(object, requestCode);
return;
}
ListString> deniedPermissions = Utils.findDeniedPermissions(Utils.getActivity(object), permissions);

if (deniedPermissions.size() > 0){
if (object instanceof Activity)
{
((Activity) object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
} else if (object instanceof Fragment)
{
((Fragment) object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
} else
{
throw new IllegalArgumentException(object.getClass().getName() + is not supported!);
}
} else {
doExecuteSuccess(object, requestCode);
}
}

代码无非是几个判断,就不做过多的解释,但是这里要和大家说点题外话,在外人看人,都是程序员,大家都能实现一个功能(任务),但是优秀的程序员会对代码逻辑判断做更多的处理。

去申请权限之后,当然就要在Activity中去重写这个方法 ,同样是最后去调用 onRequestPermissionsResult 。然后走到 MPermissions.requestResult 这个方法,接着就是 回调。 这里就概括下,因为与上个框架差不多。

private static void requestResult(Object obj, int requestCode, String[] permissions,
int[] grantResults)
{
ListString> deniedPermissions = new ArrayList();
for (int i = 0; i grantResults.length; i++)
{
if (grantResults[i] != PackageManager.PERMISSION_GRANTED)
{
deniedPermissions.add(permissions[i]);
}
}
if (deniedPermissions.size() > 0)
{
doExecuteFail(obj, requestCode);
} else
{
doExecuteSuccess(obj, requestCode);
}
}

doExecuteFail 与 doExecuteSuccess

回调成功失败与否,框架是不会在乎的,他们只会通过得到的对象强转,然后去调用。

private static void doExecuteSuccess(Object activity, int requestCode){
findPermissionProxy(activity).grant(activity, requestCode);

}

private static void doExecuteFail(Object activity, int requestCode){
findPermissionProxy(activity).denied(activity, requestCode);
}

findPermissionProxy

private static PermissionProxy findPermissionProxy(Object activity)
{
try
{
Class clazz = activity.getClass();
Class injectorClazz = Class.forName(clazz.getName() + SUFFIX);
return (PermissionProxy) injectorClazz.newInstance();
} catch (ClassNotFoundException e)
{
e.printStackTrace();
} catch (InstantiationException e)
{
e.printStackTrace();
} catch (IllegalAccessException e)
{
e.printStackTrace();
}
throw new RuntimeException(String.format(can not find %s , something when compiler., activity.getClass().getSimpleName() + SUFFIX));
}

public interface PermissionProxyT>
{
void grant(T source, int requestCode);

void denied(T source, int requestCode);

void rationale(T source, int requestCode);

boolean needShowRationale(int requestCode);
}


h2>尾声

就这样,我们已经写好两个的框架的源码分析(因为很多关于权限管理的博客),解决以及封装思路大同小异,在项目中我们可以主动去使用它们的框架,当然,如果你有一种莫名其妙的激情你也可以自己封装一个简化版的。

同时,提醒大家多去拆别人的轮子,慢慢的,你的功力就会提升了。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
  1. 免费下载或者VIP会员资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
  2. 提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。 若排除这种情况,可在对应资源底部留言,或联络我们。
  3. 找不到素材资源介绍文章里的示例图片?
    对于会员专享、整站源码、程序插件、网站模板、网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
  4. 付款后无法显示下载地址或者无法查看内容?
    如果您已经成功付款但是网站没有弹出成功提示,请联系站长提供付款信息为您处理
  5. 购买该资源后,可以退款吗?
    源码素材属于虚拟商品,具有可复制性,可传播性,一旦授予,不接受任何形式的退款、换货要求。请您在购买获取之前确认好 是您所需要的资源

评论(0)

提示:请文明发言