Android APTAndroid知多少

Android AOP你了解多少

2022-07-15  本文已影响0人  搬砖的兔子

什么是AOP

AOP(Aspect Oriented Programming)意为面向切面编程,指通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

其广泛的应用在代码的后期修改与维护之中,它对原代码侵入性少,容易扩展辅助功能,可以使原执行逻辑与改变执行逻辑解耦。

举个例子.jpeg

现在有一个智能门,以前的开门的逻辑是:输入密码 -> 拎动把手 -> 开门,现在呢业主觉得密码有可能被盗,不够安全,希望加上指纹验证。这时候我们只需要将 验证指纹 这一步插入到 开门 之前就完成了,现在的逻辑是:输入密码 -> 拎动把手 -> 指纹验证 -> 开门。这种思维就是一种面向切面的思维。

思维导图.png

什么是AspectJ

要知道AOP只是一种编程思想,那么,在android中,我们该通过何种工具来实现这种思想呢,没错,就是AspectJ。要掌握AspectJ首先要明确下面的几大概念:

AspectJ.png

Advice(通知):定义需要被注入到.class字节码文件中的代码,通俗点儿来说就是告诉编织器哪里是你需要插入的代码。

JoinPoint(连接点):即允许你插入代码的地方。

Pointcut(切入点):是对连接点的筛选与定义的一种表达式。

Pointcut.png

Aspect(切面):切面是通知和切入点的结合。

Weaving(织入):这个很好理解就是把我们定义好的切面注入到目标对象中去的过程。

基本用法

插件配置

//在根build.gradle下配置:
dependencies {
    ...
    classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8' 
}

//在app/build.gradle下配置
apply plugin: 'com.hujiang.android-aspectjx'

aspectjx {
    //关闭AspectJX功能
    enabled true
    //织入遍历符合条件的库
    //includeJarFilter 'universal-image-loader', 'AspectJX-Demo/library'
    //排除包含‘universal-image-loader’的库
    //excludeJarFilter 'universal-image-loader'
}

定义切面

创建一个类,并通过@Aspect定义为一个切面。

@Aspect
public class AspectTest {
    ......
}

创建通知、添加切点表达式

在该类中添加需要编织的方法,并通过通知和切点表达式来定义它。Ok,一个简单的防抖的OnClick判断就切入到你的程序中去了。相信其他业务你也能很好的利用AspectJ来处理了。

@Aspect
public class AspectTest {
    private static final String TAG = "AspectTest";
    @Around("execution(* android.view.View.OnClickListener.onClick(..))")
    public void onClickListener(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        Log.d(TAG, "onClick");
        if (!NoDoubleClickUtils.isDoubleClick()){
            proceedingJoinPoint.proceed();    //切回到切点并执行后续代码
        }
    }
}

补充:第三方库兼容

有时候我们在使用AspectJ的时候可能会遇到引入了一些其他的三方库的情况,而我们又需要对其内的连接点进行编织、切入。如上述点击事件防抖的例子,可能用到了Butterknife来注解@onClick(),这时你会发现上述切入表达式不起作用了,为什么呢,我们看一下Butterknife中的@OnClick定义就知道了。

@Target(METHOD)
@Retention(RUNTIME)
@ListenerClass(
    targetType = "android.view.View",
    setter = "setOnClickListener",
    type = "butterknife.internal.DebouncingOnClickListener",
    method = @ListenerMethod(
        name = "doClick",
        parameters = "android.view.View"
    )
)
public @interface OnClick {
  /** View IDs to which the method will be bound. */
  @IdRes int[] value() default { View.NO_ID };
}

我们可以发现。其通过Annotation Processor对@OnClick进行扫描,并将android.view.View.OnClickListener.onClick方法替换为butterknife.internal.DebouncingOnClickListener.doClick方法。因此为了兼容ButterKnife上述切入点表达式应该改为如下方式,及对butterknife的OnClick方法进行切入。

@Around("execution(* android.view.View.OnClickListener.onClick(..)) || execution(@butterknife.OnClick * *(..))")
public void onClickListener(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
    Log.d(TAG, "onClick");
    if (!NoDoubleClickUtils.isDoubleClick()){
        proceedingJoinPoint.proceed();
    }
}
上一篇下一篇

猜你喜欢

热点阅读