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.djoinPoint.proceed();
//        // 方法执行后的逻辑System.out.println("After method: " + joinPoint.getSignature().getName());
//
//        // 直接调用 Log.e 替换 Log.d,并阻止原方法执行
//        return Log.e(tag, "[Replaced] " + msg);
//    }/*** @After* 目标方法执行后执行,无论该方法是否抛出异常* ************************************************************************************ *//*** @AfterReturning* 在目标方法成功返回后执行,可以获取返回值。适合用于对成功结果的处理。* ************************************************************************************ *//*** @AfterThrowing* 当目标方法抛出异常时执行。可以用来进行异常处理或日志记录。* ************************************************************************************ */
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/7159.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity URP 获取/设置 Light-Indirect Multiplier

Unity URP 获取/设置 Light-Indirect Multiplier 他喵的代码的字段名称叫&#xff1a;bounceIntensity ~~~~~~

计算机网络-网络层

重点内容&#xff1a; (1) 虚拟互连网络的概念。 (2) IP 地址与物理地址的关系。 (3) 传统的分类的 IP 地址&#xff08;包括子网掩码&#xff09;和无分类域间路由选择 CIDR 。 (4) 路由选择协议的工作原理。 目录 重点内容&#xff1a; 一.网络层提供的两种服务 二…

2024年博客之星主题创作|2024年蓝桥杯与数学建模年度总结与心得

引言 2024年&#xff0c;我在蓝桥杯编程竞赛和数学建模竞赛中投入了大量时间和精力&#xff0c;这两项活动不仅加深了我对算法、数据结构、数学建模方法的理解&#xff0c;还提升了我的解决实际问题的能力。从蓝桥杯的算法挑战到数学建模的复杂应用&#xff0c;我在这些竞赛中…

虚拟头节点和双指针解决链表问题(合并,与分解操作,力扣题目为例)

Problem: 21. 合并两个有序链表 Problem: 86. 分隔链表 文章目录 总览说明题目描述思路复杂度Code总结分析 总览说明 在解决链表相关的算法题目时较多使用到的技巧就是虚拟头节点、双指针&#xff0c;而题目往往都会涉及到对链表的分解、合并操作&#xff0c;本文选择两个题目将…

Gaea项目的挑战与机遇:去中心化AI平台的未来发展

尽管Gaea在去中心化AI领域展示了巨大的潜力&#xff0c;但在实际操作中仍然面临一些挑战。首先&#xff0c;平台的用户参与度至关重要。如果用户参与的资源不足&#xff0c;平台的计算能力和带宽资源将受到限制&#xff0c;从而影响AI项目的运行效率。因此&#xff0c;如何吸引…

项目练习:若依后台管理系统-后端服务开发步骤(springboot单节点版本)

文章目录 1、用Maven搭建项目脚手架&#xff0c;父子工程依赖。2、引入SpringBoot Web容器依赖3、引入Mybatisdruid依赖4、实现接口查询数据5、整合logback日志功能6、集成Redis 1、用Maven搭建项目脚手架&#xff0c;父子工程依赖。 root模块的pom添加plugin配置 <build>…

批量创建ES索引

7.x from elasticsearch import Elasticsearch# 配置 Elasticsearch 连接 # 替换为你的 Elasticsearch 地址、端口、用户名和密码 es Elasticsearch([http://10.10.x.x:43885],basic_auth(admin, XN272G9THEAPYD5N5QORX3PB1TSQELLB) )# # 测试连接 # try: # # 尝试获取集…

ansible自动化运维实战--script、unarchive和shell模块(6)

文章目录 一、script模块1.1、功能1.2、常用参数1.3、举例 二、unarchive模块2.1、功能2.2、常用参数2.3、举例 三、shell模块3.1、功能3.2、常用参数3.3、举例 一、script模块 1.1、功能 Ansible 的 script 模块允许你在远程主机上运行本地的脚本文件&#xff0c;其提供了一…

【2024年终总结】深圳工作生活评测

距离上次写年终总结已经过了一年半了&#xff0c;这一年半中哪怕经历了很多的事情&#xff0c;但是感觉又没发生什么。想写一些骚话&#xff0c;却总觉得自己无法完全表达&#xff0c;便也就这样&#xff0c;静静地记录下这一段时光。 现在是2025年&#xff0c;春节前的时光&am…

VSCode+Continue实现AI辅助编程

Continue是一款功能强大的AI辅助编程插件&#xff0c;可连接多种大模型&#xff0c;支持代码设计优化、错误修正、自动补全、注释编写等功能&#xff0c;助力开发人员提高工作效率与代码质量。以下是其安装和使用方法&#xff1a; 一、安装VSCode 参见&#xff1a; vscode安…

【游戏设计原理】82 - 巴斯特原则

巴斯特原则的核心是“对你的玩家好一点”&#xff0c;这一点直击游戏设计的核心——玩家体验。 现代游戏设计不仅要注重挑战性&#xff0c;还要关注玩家的情绪波动与行为反应。当玩家因为过高的难度感到挫败甚至愤怒时&#xff0c;他们往往选择退出游戏&#xff0c;而不是迎接…

C++内存分布与进程地址空间

C内存分布与进程地址空间 1.C/C内存分布2.进程地址空间&#xff08;补充&#xff09; &#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x1f680;&#x1f680;系列专栏&#xff1a;【Linux的学习】 &#x1f4dd;&#x1f…

C语言内存管理详解

C语言不像其他高级语言那样提供自动内存管理&#xff0c;它要求程序员手动进行内存的分配和释放。在C语言中&#xff0c;动态内存的管理主要依赖于 malloc、calloc、realloc 和 free 等函数。理解这些函数的用法、内存泄漏的原因及其防止方法&#xff0c;对于编写高效、可靠的C…

头像生成小程序搭建(免费分享)

如下图为小程序页面的基本效果&#xff0c;下面将介绍该小程序的功能 页面template代码如下&#xff1a; <template><view class"avatar-containner"><block v-if"!showCropper"><image class"pageback" src"../../s…

使用 Confluent Cloud 的 Elasticsearch Connector 部署 Elastic Agent

作者&#xff1a;来自 Elastic Nima Rezainia Confluent Cloud 用户现在可以使用更新后的 Elasticsearch Sink Connector 与 Elastic Agent 和 Elastic Integrations 来实现完全托管且高度可扩展的数据提取架构。 Elastic 和 Confluent 是关键的技术合作伙伴&#xff0c;我们很…

Spring 定时任务:@Scheduled 注解四大参数解析

本文主要介绍了在 Spring 框架中使用Scheduled注解实现定时任务的方法&#xff0c;重点讲解了fixedRate、fixedDelay、cron和initialDelay这四个参数的用法&#xff0c;并通过实例代码进行了详细说明。 1. fixedRate 参数 参数含义 fixedRate指定任务固定时间间隔执行。如设…

刷题总结 回溯算法

为了方便复习并且在把算法忘掉的时候能尽量快速的捡起来 刷完回溯算法这里需要做个总结 回溯算法的适用范围 回溯算法是深度优先搜索&#xff08;DFS&#xff09;的一种特定应用&#xff0c;在DFS的基础上引入了约束检查和回退机制。 相比于普通的DFS&#xff0c;回溯法的优…

【MySQL】我在广州学Mysql 系列——MySQL用户管理详解

ℹ️大家好&#xff0c;我是练小杰&#xff0c;本博客是春节前最后一篇了&#xff0c;在此感谢大佬们今年的支持&#xff01;&#xff01;&#x1f64f;&#x1f64f; 接下来将学习MYSQL用户管理的相关概念以及命令~~ 回顾&#xff1a;&#x1f449;【MYSQL触发器的使用】 数据…

网络编程-网络原理HTTP1

文章目录 HTTP请求/响应的基本结构认识URLURL是什么和基本格式关于encoding机制 认识方法(method)GET方法简介GET方法的特点POST方法简介POST方法的特点GET和POST的区别(经典面试题)关于GET和POST的补充说明Restful风格 上节主要是对http协议的一些最基本的概念做出一些说明, 然…

概率密度函数(PDF)分布函数(CDF)——直方图累积直方图——直方图规定化的数学基础

对于连续型随机变量&#xff0c;分布函数&#xff08;Cumulative Distribution Function, CDF&#xff09;是概率密度函数&#xff08;Probability Density Function, PDF&#xff09;的变上限积分&#xff0c;概率密度函数是分布函数的导函数。 如果我们有一个连续型随机变量…