网站站点地图设计,建立网站需要多少钱?,哪些网站可以做h5,sem是什么加入引用
在整个项目的 build.gradle 中#xff0c;添加
classpath com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10 可以看到测试demo的 gradle 版本是很低的。
基于 github 上的文档#xff0c;可以看到原版只支持到 gradle 4.4 。后续需要使…加入引用
在整个项目的 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.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;/*** 切入点学习* 所有类切入点 (* com.aaaa.testplayer..*(..))* 1. execution(...):* 这是一个切点表达式用于匹配方法执行的连接点。* p* 2. * (返回值类型):* 这个星号表示“任意返回类型”。也就是说这个切点将匹配任何返回类型的方法无论是 void、int、String 等。* p* 3. com.aaaa.testplayer..* (声明类型模式):* com.aaaa.testplayer: 这是你的包名。* ..: 这个符号表示“零个或多个子包”。它允许在 com.aaaa.testplayer 包及其所有子包中匹配类。例如它会匹配* com.aaaa.testplayer.MyClass* com.aaaa.testplayer.subpackage.AnotherClass* *: 这个星号表示“任意类名”。它允许匹配该包下的所有类。* p* 4. ( .. ) (参数模式):* .. 代表“零个或多个参数”。这意味着该切点会匹配任何数量的参数包括没有参数的情况。比如* myMethod()* myMethod(int a)* myMethod(String a, int b, double c)* p* p* 另一种实现** Pointcut(execution(* com.aaaa.testplayer.*.*(..)))* 匹配的是 com.example.service 包下的所有类和方法。* *.*(..) 的意思是匹配该包下任何类中的任何方法不论返回类型、方法名称或参数类型。*//**** 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 {/*** Before 在指定位置之前进行触发* ************************************************************************/// 在指定的 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());
// }/*** Around 在运行中触发* ************************************************************************//*** 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 差异不大但避免了冗余的原方法调用。* */// 将全局的log.d 调用替换为 Log.eAround(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* 目标方法执行后执行无论该方法是否抛出异常。能够用于执行一些清理工作、记录日志等* 无法直接获取方法的返回值没有 JoinPoint* ************************************************************************************ */// Log 方法调用后输出日志。 这里用 println 避免死循环
// After(call(* android.util.Log.*(String, String)))
// public void afterAdvice() {
// System.out.println(Log method execution);
// }/*** AfterReturning* 在目标方法成功返回后执行可以获取返回值。适合用于对成功结果的处理。* ************************************************************************************ */// 指定方法运行完成后获取返回值
// AfterReturning(pointcut execution(* com.aaaa.testplayer.MainActivity.getMsg()), returning result)
// public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
// Log.e(aaaaa,AfterReturning Method executed: joinPoint.getSignature().getName());
// Log.e(aaaaa,AfterReturning Returned value: result);
//
// // 获取方法参数
// Object[] args joinPoint.getArgs();
// System.out.println(MAfterReturning ethod arguments: );
// for (Object arg : args) {
// Log.e(aaaaa,AfterReturning Argument: arg);
// }
// }/*** AfterThrowing* 当目标方法抛出异常时执行。可以用来进行异常处理或日志记录。* ************************************************************************************ */// 定义切点匹配所有抛出异常的方法
// AfterThrowing(pointcut execution(* com.aaaa.testplayer..*(..)), throwing ex)
// public void logAfterThrowing(Exception ex) {
// Log.e(aaaaa, 发现exception ex.getMessage());
// // 这里可以添加其他处理逻辑比如发送通知、记录到数据库等
// }
}
反编译后可以看到对应的位置前新增了代码 多个切点的处理
可以预先定义 Pointcut 也可以对定义的 Pointcut 进行复用。 关于第三方库
aspectjx 可以对引入的三方库也生效但是对已经编译好的 Android 系统类例如 TextView无法进行动态修改。这里使用三方库 autosize 来示例。
因为 autosize 在部分设备上显示有问题需要调试。autosize 库默认对日志进行 debug 级别输出但是在某些设备上系统只输出 info 级别及以上的 log导致 autosize 日志全无这里借助工具将 me.jessyan.autosize.utils.AutoSizeLog 中的日志级别强制提到 error 级。
首先需要引入三方库 然后在 aspectx 中记得加入对应的包名不然织入不会生效。 编写替换代码这里将全局的 Log.d 都替换为 Log.e 了。这个代码上面有示例 运行后即可看到日志都被提升到 error 反编译apk查看 可以看到 debug 级别输出的代码已经被替换。 warn 和 err 不受影响 关于高 gradle 版本
根据github 官方文档gradle 支持只到 4.4 。那么高 gradle 需要额外处理
强制使用 gradle 7.1.3
最小的改动就是将 gradl 版本指定为 7.1.3 .之前我用的 7.2.2 不行会报 Failed to apply plugin ‘android-aspectjx’. No such property: FD_INTERMEDIATES for class: com.android.builder.model.AndroidProject gradle7.2及以上可使用wurensen重构过的版本来实现AOP操作。
参考GitHub - LZ9/AspectjxDemo: 简单的Aspectjx接入demo兼容gradle7以上版本 classpath io.github.wurensen:gradle-android-plugin-aspectjx:3.3.2 apply plugin: io.github.wurensen.android-aspectjx aspectjx {enabled trueinclude com.aaaa.testplayerexclude androidx, com.google, com.squareup, com.alipay, com.taobao, org.apache,kotlinx, org.jetbrains, module-info, versions.9
}