注解

AOP编程_Android优雅权限框架(2)Demo完全解析

2019-11-22  本文已影响0人  享学课堂

享学课堂特邀作者:老顾
转载请声明出处!

上篇文章:AOP编程_Android优雅权限框架(1)概念基础
https://www.jianshu.com/p/46cc0840a775

5. AOP优雅权限框架详解

Demo地址:https://github.com/18598925736/GracefulPermissionFramework/tree/dev_aspectJ

gradle配置

Java代码

3个注解 @PermissionDenied @PermissionDeniedForever @PermissionNeed

  /**
 * 被此注解修饰的方法,会在方法执行之前去申请相应的权限,只有用户授予权限,被修饰的方法体才会执行
 */
@Target(ElementType.METHOD)//此注解用于修饰方法
@Retention(RetentionPolicy.RUNTIME)//注解保留到运行时,因为可能会需要反射执行方法(上面说了修饰的是方法!)
public @interface PermissionNeed {

    String[] permissions();//需要申请的权限,支持多个,需要传入String数组

    int requestCode() default 0;//此次申请权限之后的返回码
}

/**
 * 被此注解修饰的方法,会在权限申请失败时被调用
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionDenied {
}

/**
 * 被此注解修饰的方法,会在用户永久禁止权限之后被调用
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionDeniedForever {
}

处理权限回调结果的接口 IPermissionCallback

/**
 * 权限申请结果接口
 */
public interface IPermissionCallback {

    /**
     * 授予权限
     */
    void granted(int requestCode);

    /**
     * 这次拒绝,但是并没有勾选"以后不再提示"
     */
    void denied(int requestCode);

    /**
     * 勾选"以后不再提示",并且拒绝
     */
    void deniedForever(int requestCode);
}

以上都是事先要预备好的东西,接下来进入核心

PermissionAspect

@Aspect
public class PermissionAspect {

    private static final String TAG = "PermissionAspectTag";

    private final String pointcutExpression
            = "execution(@com.zhou.zpermission.annotation.PermissionNeed * *(..)) && @annotation(permissionNeed)";

    @Pointcut(value = pointcutExpression)
    public void requestPermission(PermissionNeed permissionNeed) {
        Log.d(TAG, "pointCut 定义切入点");
    }

    @Around("requestPermission(permissionNeed)")
    public void doPermission(final ProceedingJoinPoint joinPoint, PermissionNeed permissionNeed) {
        PermissionAspectActivity.startActivity(getContext(joinPoint), permissionNeed.permissions(), permissionNeed.requestCode(), new IPermissionCallback() {
            @Override
            public void granted(int requestCode) {
                // 如果授予,那么执行joinPoint原方法体
                try {
                    joinPoint.proceed();
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
            }

            @Override
            public void denied(int requestCode) {//这里的getThis也是要给梗
                PermissionUtil.invokeAnnotation(joinPoint.getThis(), PermissionDenied.class, requestCode);
            }

            @Override
            public void deniedForever(int requestCode) {
                PermissionUtil.invokeAnnotation(joinPoint.getThis(), PermissionDeniedForever.class, requestCode);
            }
        });
    }

    private Context getContext(final ProceedingJoinPoint joinPoint) {
        final Object obj = joinPoint.getThis();
        if (obj instanceof Context) {// 如果切入点是一个类?那么这个类的对象是不是context?
            return (Context) obj;
        } else {// 如果切入点不是Context的子类呢? //jointPoint.getThis,其实是得到切入点所在类的对象
            Object[] args = joinPoint.getArgs();
            if (args.length > 0) {//
                if (args[0] instanceof Context) {//看看第一个参数是不是context
                    return (Context) args[0];
                } else {
                    return ApplicationUtil.getApplication();//如果不是,那么就只好hook反射了
                }
            } else {
                return ApplicationUtil.getApplication();//如果不是,那么就只好hook反射了
            }

        }
    }
}

此段代码解读如下:

PermissionAspect类的作用是:定义切入点和切入策略,那么现在我们确定切入点是 被注解@PermissionNeed修饰的方法,切入策略是@Around,那么,切入之后我们做了哪些事呢?

接下往下看…

解读:

  1. 提供一个静态方法

public static void startActivity(Context context, String[] permissions, int requestCode, IPermissionCallback callback)

用于启动自己 PermissionAspectActivity,

接收的参数分别为:context,需要的权限数组,权限返回码,权限结果回调接口

  1. onCreate方法中,检查是否已经有想要申请的权限,如果有,直接调用mCallback.granted(requestCode); 并且结束自身,并且要注意隐藏Activity的切换动画。如果没有,那么,就去requestPermissions(permissions, requestCode);申请权限。
  1. 处理权限申请的回调,并且分情况调用mCallback的回调方法,然后结束自身

需要注意

PermissionAspectActivity必须在module的清单文件中注册

并且 要定义它的theme使得Activity完全透明

Gif图效果演示:

6. AOP思想以及常用AOP框架

所谓AOP(ApsectOrientedProgramming) 面向切面编程。

此概念是基于OOP (ObjectOrientiedProgramming)面向对象编程。在OOP中,我们可以把不同的业务功能都分成一个一个的模块,然后每一个模块有自己的专一职责,从而优化编程过程,降低编程犯错几率。但是随着OOP类的数量的增加,我们会发现,在某一些业务类中,经常有一些相同的代码在重复编写,但是无可奈何,比如日志打印/动态权限申请/埋点数据上报/用户登录状态检查 /服务器端口连通性检查 等等。这些代码,我们虽然可以他们抽离出来整理到一个个专一的模块中,但是调用的时候,还是到处分散的,并且这些调用还入侵了本来不直接相关的业务代码,让我们阅读业务代码十分费劲。

而AOP的出现,就是基于OOP的这种缺陷而出现的优化方案。利用AOP,我们可以对业务逻辑的各个部分进行隔离,使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高开发效率,减少犯错概率。

画图表示:

如上图,OOP中,同样的一段过程,我们把登录检查,权限检查,埋点上报的调用代码写了3遍,然而都是雷同代码,只是参数不同而已。而,换成AOP的思想来编码。则是如下:

所采取的方案为:

在class A , B, C中 找到切入点,然后在切入点插入共同的逻辑,而不是多次编写雷同的代码.

本文的Demo中,插入相同的逻辑,使用的是 Java自定义注解+@Aspect切面类+@PointCut切入点+@Around切入策略 的方式。这只是AOP方案的一种,叫做 AspectJ

除此之外,Android开发中常用的AOP方案还有:

(Java注解存在3个阶段,一个是源码期,一个是编译期,一个运行期)

  • APT

Java的注解解析技术(AnnotationProcessingTool), Apt的作用时期,是 通过 自定义注解解析类(extends AbastractProcessor),对自定义注解进行解析,然后通过JavaPoet这种java类生成工具,来生成编译期才会有的.java(源码中并没有),然而我们源码中却可以使用这个类。

  • ASM

Asm是Java的字节码操作框架,它可以动态生成类或者增强既有类的功能。理论上,它可以对class文件做任何他想做的事。包括,改变class文件的内容,或者生成新的class。严格来说AspectJ底层就是ASM,只不过AspectJ帮我们做了ASM框架做起来很麻烦,容易出错的事情,让我们可以简单的通过 @Aspect @PointCut @Around 这样的注解,就能完成AOP面向切面编程。但是,ASM作为AspectJ的祖宗,某些时候依然可以完成AspectJ所无法触及到的功能, 就像是c/c++作为Java的祖宗, 现在依然有自己不可替代的作用。


7. AspectJ AOP框架的深入原理研究

…本来想写成一篇,但是发现篇幅太长,留个尾巴,下一篇,解析AspectJ是如何通过@注解的方式来插入逻辑的。


喜欢本文的话可以关注我们的官方账号,第一时间获取资讯。
你的关注是对我们更新最大的动力哦~

上一篇下一篇

猜你喜欢

热点阅读