Android切面编程AOP之AspectJ的使用

概述

AOP(Aspect Oriented Programming)面向切面编程。就是:在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想。

使用场景

针对很多地方都需要用到的模板代码,我们都可以用AOP来做,比如:
日志性能监控埋点等。
举个列子,很多时候我们需要根据当前网络情况做下一步操作,代码如下:

    public void checkNetworkNormal(){
        if (NetworkUtils.isNetworkAvailable(this)) {
            Log.i(TAG, "手机有网,可以进入下一个页面");
        } else {
            Log.i(TAG,"请检查手机网络设置");
        }
    }

而如果使用aop,情况就可以变成下面这样:

    @CheckNetwork()
    public void checkNetwork(){
        Log.i(TAG, "手机有网,可以进入下一个页面");
    }

没有网络的情况我们就注解统一处理了,在需要检查的地方加个注解,而不用在每次需要检查的时候写 if / else

方案选型

AOPSpring中使用已很久了,AOP只是一种方法论,就跟OPP是一种思想,而JavaOOP的一种,AOP的实践方案有很多,比如:APTAspectJJavassistASMXposedDexposed等;不过在Android平台上最常用的还是三剑客:APTAspectJJavassist

方案分类

根据织入时机,我们可以把相关框架分类如下:

织入时机技术框架
静态织入APT,AspectJ、ASM、Javassit
动态织入java动态代理,cglib、Javassit

他们的区别
静态织入: 发生在编译期,改变的是class字节码,因此几乎不会对运行时的效率产生影响;
动态织入:发生在运行期,将字节码写入内存,并通过反射完成类的加载,所以效率相对较低,但更灵活。

执行时机
  • 各方案织入时机图示,一目了然 :-

各代码生成工具运行时机

作者:Czppp
链接:https://www.jianshu.com/p/3de31e13f2fa
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方案对比

总结下各框架的优劣势及学习维护成本,根据需求酌情选择 ~ 。~

技术框架特点开发难度优势不足
APT常用于通过注解减少模板代码,对类的创建于增强需要依赖其他框架。★★开发注解简化上层编码。使用注解对原工程具有侵入性。
AspectJ提供完整的面向切面编程的注解。★★真正意义的AOP,支持通配、继承结构的AOP,无需硬编码切面。重复织入、不织入问题,不支持java8
ASM面向字节码指令编程,功能强大。★★★高效,ASM5开始支持java8。切面能力不足,部分场景需硬编码。
JavassitAPI简洁易懂,快速开发。上手快,新人友好,具备运行时加载class能力。切点代码编写需注意class path加载问题。
java动态代理运行时扩展代理接口功能。运行时动态增强。仅支持代理接口,扩展性差,使用反射性能差。

作者:wanderingGuy
链接:https://www.jianshu.com/p/0acd18d50717
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android上最早应用的可以说是 JakeWharton/hugo ,使用的是AspectJ,原理介绍的很多

AspectJ的使用

查看最新版本:AspectJ官网

侵入式

一般使用注解(即在需要插桩的地方添加一个注解),在指定点改变原有行为的方式。

示例
  1. 我们先定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface Check {
    String[] value() default {};
}
  1. 注解解释器(如何改变切点的行为)
@Aspect
public class CheckAspectj {
//....
@Around("(method() || constructor()) && @annotation(check)")
    public Object aroundJoinPoint(ProceedingJoinPoint joinPoint, Check check) throws Throwable {
    //....
}
//....
}
  1. 在需要的地方添加注解
@Check({"1"})
private void doSomeThing1() {
    Log.d(TAG, "passed === doSomeThing1: ");
}

非侵入式

不需要添加注解、通常使用在外部sdk系统源码,我们不方便修改,又希望改变它的原有行为

示例
@Around("call(* android.widget.TextView.setText(java.lang.CharSequence))")
    public void handleSetText(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Log.d(TAG," start handleSetText");
        proceedingJoinPoint.proceed(new Object[]{"处理过的text"});
        Log.d(TAG," end handleSetText");
    }

这个就会修改掉我们给TextView设置的text,替换为处理过的text

自定义Gradle插件

看了一些网上的介绍,对于AspectJ的集成,我们需要用到 AspectJ 的编译器,而且有一些插件,比如:

classpath 'org.aspectj:aspectjweaver:1.9.7'
classpath 'org.aspectj:aspectjtools:1.9.7'
implementation 'org.aspectj:aspectjrt:1.9.7'

这里面我们可以查看源码得知:jweaver 是包含 toolsjtools是包含 jrt
通过名字我们就可以看出和Java类似:JDKJRE

gradle-plugin三步曲

编写 groovy 文件
class AOPPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
		//....
    }
}
resources/META-INFO/gradle-plugins/xxx.properties
implementation-class=com.bobomee.aop_plugin.AOPPlugin
build.gradle
plugins {
    id 'groovy'
}

dependencies {
    implementation gradleApi()
    implementation localGroovy()
    implementation 'com.android.tools.build:gradle:7.0.2'
    implementation 'org.aspectj:aspectjtools:1.9.7'
}

apply from: "./localRepository.gradle"

到此我们的 自定义 gradle plugin就完成了,其他地方需要用的时候,只需要 apply plugin 就行了

plugins {
    id 'com.android.library'
    id 'aop'  // 我们的插件
}

maven-publish

把插件上传maven,方便其他项目使用,可以使用 maven-publish ,示例中我 将 annotationruntimeplugin都编译为maven形式进行了打包, 用法参考 :maven-publish官网doc

示例
CheckerHolder.setChecker(new Checker() {
            @Override
            public boolean check(String[] strings, JoinPoint joinPoint) {
                if (TextUtils.equals("1", strings[0])) {
                    Log.e(TAG, "check: === intercept === " + strings[0]);
                    return true;
                } else if (TextUtils.equals("2", strings[0])) {
                    Log.e(TAG, "check: === intercept === " + strings[0]);
                    return false;
                }
                return false;
            }
        });
@Check({"2"})
private void doSomeThing2(String str) {
     Log.d(TAG, "doSomeThing2: === passed == "+str);
}

@Check({"1"})
private void doSomeThing1() {
     Log.d(TAG, "passed === doSomeThing1: ");
}
   /**
     * 在MainActivity的所有生命周期的方法中打印log
     */
    @Before("execution(* android.app.Activity.**(..))")
    public void lifeCycle(JoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String className = joinPoint.getThis().getClass().getSimpleName();
        Log.e(TAG, "class:" + className+" method:" + methodSignature.getName());
    }

示例代码:Github/Aop-Demo
学习资料:深入理解Android之AOP

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值