当前位置: 首页 > article >正文

Android AOP:aspectjx

加入引用

在整个项目的 build.gradle 中,添加 

classpath "com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10"

 可以看到测试demo的 gradle 版本是很低的。

基于 github 上的文档,可以看到原版只支持到 gradle 4.4 。后续需要使用社区版的 aspectjx

GitHub - HujiangTechnology/gradle_plugin_android_aspectjx: A Android gradle plugin that effects AspectJ on Android project and can hook methods in Kotlin, aar and jar file.

然后在App 目录下的 build.gradle 中加入plugin 标记即可。

apply plugin: 'android-aspectjx'

还可以指定需要生效的位置,块放在最后即可。

include 生效的包名

exclude 排除的包名


使用

基于 @Aspect 注解,LogAspect  不需要在任何地方调用,自动会织入。

package com.aaaa.testplayer;

import android.util.Log;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.SourceLocation;

import java.util.Arrays;

import javax.crypto.Mac;

/**
 * 切入点学习:
 *  所有类切入点:  (* com.aaaa.testplayer..*(..))
 *  1. execution(...):
 *      这是一个切点表达式,用于匹配方法执行的连接点。
 *
 *  2. * (返回值类型):
 *      这个星号表示“任意返回类型”。也就是说,这个切点将匹配任何返回类型的方法,无论是 void、int、String 等。
 *
 *  3. com.aaaa.testplayer..* (声明类型模式):
 *      com.aaaa.testplayer: 这是你的包名。
 *       ..: 这个符号表示“零个或多个子包”。它允许在 com.aaaa.testplayer 包及其所有子包中匹配类。例如,它会匹配:
 *          com.aaaa.testplayer.MyClass
 *          com.aaaa.testplayer.subpackage.AnotherClass
 *      *: 这个星号表示“任意类名”。它允许匹配该包下的所有类。
 *
 *  4. ( .. ) (参数模式):
 *      .. 代表“零个或多个参数”。这意味着该切点会匹配任何数量的参数,包括没有参数的情况。比如:
 *          myMethod()
 *          myMethod(int a)
 *          myMethod(String a, int b, double c)
 *
 *
 * */


/**
 *
 * JoinPoint :
 * 1. 获取上下文信息:通过 JoinPoint,你可以获取关于当前连接点的信息,比如方法名、类名、参数等等。
 * 2. 不能控制流程:JoinPoint 只能用来查看信息,而不能控制方法的执行。
 *
 * ProceedingJoinPoint:
 * 1. 控制方法执行:你可以使用 proceed() 方法来继续执行原始方法,或者选择不执行它。
 * 2. 获取上下文信息:像 JoinPoint 一样,ProceedingJoinPoint 也可以访问连接点的信息,但它还额外提供了一些可以控制的功能。
 *
 *
 * JoinPoint 的使用,具体可以参考:beforeAdvice
 *
 * ProceedingJoinPoint主要多了 proceed()方法,配合 @Around 环绕,可以控制方法是否执行
 *
 * */


/**
 * 关于 throws Throwable 。可以使用 try/catch 组合来代替,主要取决于是否要向上传递 error
 * */


/**
 * @Before: 在目标方法执行之前。
 * @After: 在目标方法执行之后,无论结果如何。
 * @AfterReturning: 在目标方法成功返回之后。
 * @AfterThrowing: 在目标方法抛出异常之后。
 * @Around: 可自定义目标方法的执行过程,包括前置和后置操作。
 * */
@Aspect
public class LogAspect {

    /**
     * 在指定位置之前进行触发
     * ************************************************************************/

    // 在指定的 class 方法运行前,进行log输出
//    @Before("execution(* com.aaaa.testplayer.MainActivity.onCreate(..))")
//    public void beforeOnCreate() {
//        Log.e("aaaaa","MainActivity onCreate called");
//    }

    /**
     * 切入点学习:
     *  1. * android.util.Log.d(String, String):
     *      * 表示“任意返回类型”,在这里它表示 Log.d 方法可以是任何返回类型(由于 Log.d 返回 int 作为日志行 ID,但我们通常不关心返回值)。
     *
     *  2. android.util.Log.d: 这是我们要匹配的具体方法,即 Android 的 Log 类的 d 方法,表示 "debug" 日志。
     *      (String, String): 表示该方法接受两个 String 类型的参数。也就是说,它只会匹配传递两个 String 参数的 Log.d 方法调用。
     *
     *  3.&& args(tag, msg):
     *      && 是逻辑与操作符,表示该切点表达式的两个部分必须同时满足。
     *      args(tag, msg): 这个部分用于提取通过方法参数传入的数据。tag 和 msg 是这两个参数的名称,可以在通知方法中使用。这允许你在切面中引用这些参数,使得你能够对子日志信息进行处理或记录。
     *
     *   如果 不加 && args(tag, msg),那么下面就无法获取整个参数!!!
     * */
    // 在指定的方法运行前,进行log输出
//    @Before("call(* android.util.Log.d(String, String)) && args(tag, msg)")
//    public void replaceLog(String tag, String msg) {
//        // 将所有 Log.d 前增加为 Log.e
//        Log.e(tag, "[Replaced] " + msg);
//    }




    // 在所有方法执行前被调用,并打印出正在执行的方法名称。
//    @Before("execution(* com.aaaa.testplayer..*(..))")
//    public void beforeAdvice(JoinPoint joinPoint) {
//        // 获取方法名
//        String methodName = joinPoint.getSignature().getName();
//        // 可以用来获得当前执行的方法所在的类的全名
//        String methodDeclaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
//        // 用于在运行时获取方法的声明类对象,从而可以调用该类的方法、访问其字段等。
//        Class<?> declaringType = joinPoint.getSignature().getDeclaringType();
//        // 注意这里因为劫持了所有方法所以要检查 methodName, 不然会死循环
//        if (declaringType == MainActivity.class && methodName == "onCreate") {
//            MainActivity mainActivity = (MainActivity) joinPoint.getTarget(); // 获取目标对象
//            try {
//                // 调用 showMsg 方法
//                mainActivity.showMsg("This is a message from the aspect!");
//            } catch (Exception e) {
//                Log.e("AspectError", "Error calling showMsg method", e);
//            }
//        }
//
//        // 获取目标对象
//        Object targetObject = joinPoint.getTarget();
//
//        // 获取代理对象
//        Object proxyObject = joinPoint.getThis();
//
//        // 获取参数
//        // 可以获取到入参,例如在 public void onTest1(View view) 中可以获取到入参  android.widget.Button{f2aba96 VFED..C.. .F....ID 0,0-300,90}
//        Object[] args = joinPoint.getArgs();
//
//        // 打印信息到 Logcat
//        Log.e("aaaaa", "Method name: " + methodName);
//        Log.e("aaaaa", "methodDeclaringTypeName name: " + methodDeclaringTypeName);
//        Log.e("aaaaa", "目标对象 Target object: " + targetObject.getClass().getSimpleName());
//        Log.e("aaaaa", "代理对象 Proxy object: " + proxyObject.getClass().getSimpleName());
//        Log.e("aaaaa", "Arguments: " + Arrays.toString(args));
//        if (args.length > 0) {
//            for (Object arg : args) {
//                if (arg == null){
//                    continue;
//                }
//                Log.e("Aspect", "Argument type: " + arg.getClass().getSimpleName() + ", value: " + arg);
//            }
//        }
//
//        // 连接点描述
//        Log.e("aaaaa", ("JoinPoint description: " + joinPoint.toString()));
//
//        // 获取源位置(文件名和行号)
//        SourceLocation location = joinPoint.getSourceLocation();
//        Log.e("aaaaa", "Source location: " + location.getFileName() + ":" + location.getLine());
//    }

    /**
     * 在运行中触发
     * ************************************************************************/


    /**
     * 1. @Around 通知
     * 允许你完全控制目标方法的执行,包括是否调用原方法(通过 joinPoint.proceed())。
     *
     * 2.不调用 proceed()
     * 这里我们直接返回 Log.e 的结果,不执行 joinPoint.proceed(),从而完全跳过原始 Log.d 的执行。
     *
     * 3.返回值处理
     * Log.d 和 Log.e 都返回 int(日志的优先级/类型),因此直接返回 Log.e 的返回值是类型安全的。如果调用代码依赖返回值,也能保持一致。
     *
     * 4.参数注入
     * 通过 args(tag, msg) 将原始参数注入方法,你可以在新逻辑中复用或修改它们。
     *
     * @Around 在性能上与 @Before 差异不大,但避免了冗余的原方法调用。
     * */
//    @Around("call(* android.util.Log.d(String, String)) && args(tag, msg)")
//    public Object replaceLog(ProceedingJoinPoint joinPoint, String tag, String msg) throws Throwable {
//        // 方法执行前的逻辑
        System.out.println("Before method: " + joinPoint.getSignature().getName());
//        // 这里决定了是否要调用原有的参数。如果调用这句,约等于前面的 before ,不过会先打出log.d
        joinPoint.proceed();
//        // 方法执行后的逻辑
        System.out.println("After method: " + joinPoint.getSignature().getName());
//
//        // 直接调用 Log.e 替换 Log.d,并阻止原方法执行
//        return Log.e(tag, "[Replaced] " + msg);
//    }


    /**
     * @After
     * 目标方法执行后执行,无论该方法是否抛出异常
     * ***********************************************************************************
     * */



    /**
     * @AfterReturning
     * 在目标方法成功返回后执行,可以获取返回值。适合用于对成功结果的处理。
     * ***********************************************************************************
     * */


    /**
     * @AfterThrowing
     * 当目标方法抛出异常时执行。可以用来进行异常处理或日志记录。
     * ***********************************************************************************
     * */
}

反编译后可以看到对应的位置前新增了代码 


http://www.kler.cn/a/518775.html

相关文章:

  • 第五节 MATLAB命令
  • Oracle Agile PLM Web Service Java示例测试开发(一)环境环境、准备说明
  • CentOS 7 安装fail2ban hostdeny方式封禁ip —— 筑梦之路
  • ray.rllib-入门实践-12:自定义policy
  • Electron学习笔记,安装环境(1)
  • redis分布式锁
  • 二叉树的最小深度力扣--111
  • 嵌入式MCU面试笔记2
  • HBase的原理
  • c#使用Confluent.Kafka实现生产者发送消息至kafka(远程连接kafka发送消息超时的解决 Local:Message timed out)
  • 9.像素概念
  • 利用机器学习创建基于位置的推荐程序
  • 自定义数据集 使用tensorflow框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测
  • 【Linux】命令为桥,存在为岸,穿越虚拟世界的哲学之道
  • adc和dma的使用原理和过程
  • L9305驱动组件接口TESSY测试
  • OpenFGA
  • 小识JVM堆内存管理的优化机制TLAB
  • 基于模糊PID的孵化箱温度控制系统(论文+源码)
  • vue3 vue2区别
  • C#常考随笔1:const和readonly有什么区别?
  • [笔记] 极狐GitLab实例 : 手动备份步骤总结
  • Windows11离线安装wsl2(适用于 Linux 的 Windows 子系统)和Ubuntu
  • Android Toast 系统切换多语言显示
  • 当AI风暴来袭:中美科技商业版图的迥异走向
  • MyBatis 写法