AspectJ 在 Android 中的完整使用指南

news2025/6/12 9:39:12

一、环境配置(Gradle 7.0+ 适配)

1. 项目级 build.gradle
// 注意:沪江插件已停更,推荐官方兼容方案
buildscript {
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.9.9.1' // AspectJ 工具
    }
}

2. 模块级 build.gradle

plugins {
    id 'com.android.application'
    id 'io.freefair.aspectj' // 官方推荐插件(支持 AGP 7.0+)
}

dependencies {
    implementation 'org.aspectj:aspectjrt:1.9.9.1' // 运行时库
}

兼容性说明:若项目含 Kotlin,添加 id 'io.freefair.aspectj.post-compile-weaving' 插件

二、核心语法详解

1. 切点表达式(Pointcut)
表达式示例含义
execution(public * *(..))所有 public 方法
execution(* com.util.*.*(..))com.util 包下所有方法
@annotation(com.LogTrack)被 @LogTrack 注解的方法
within(com.ui..*)com.ui 包及其子包所有类的方法
call(* android.util.Log.d(..))拦截 Log.d() 的调用(非执行)
2. 通知类型(Advice)
@Before("pointcut()")          // 方法执行前
@After("pointcut()")           // 方法执行后(无论成败)
@AfterReturning(pointcut="", returning="result") // 方法返回后
@AfterThrowing(pointcut="", throwing="ex") // 抛出异常后
@Around("pointcut()")          // 完全控制方法执行(最常用)

三、实战案例

案例 1:自动日志追踪
@Aspect
public class DebugLogAspect {

    // 切点:标记 @DebugLog 的方法
    @Pointcut("execution(@com.example.DebugLog * *(..))")
    public void debugLogPointcut() {}

    @Around("debugLogPointcut()")
    public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getMethod().getName();
        long start = System.currentTimeMillis();
        
        Log.d("AspectJ", "▶️ " + methodName + " 开始 | 参数: " + Arrays.toString(joinPoint.getArgs()));
        
        Object result = joinPoint.proceed(); // 执行原方法
        
        long duration = System.currentTimeMillis() - start;
        Log.d("AspectJ", "◀️ " + methodName + " 结束 | 耗时: " + duration + "ms | 结果: " + result);
        
        return result;
    }
}
案例 2:权限申请自动化
// 定义权限注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequirePermission {
    String[] value(); // 需要申请的权限
}

// 切面实现
@Aspect
public class PermissionAspect {
    
    @Around("execution(@RequirePermission * *(..)) && @annotation(permission)")
    public Object checkPermission(ProceedingJoinPoint pjp, RequirePermission permission) throws Throwable {
        Activity activity = (Activity) pjp.getTarget(); // 获取宿主Activity
        String[] perms = permission.value();
        
        if (hasPermissions(activity, perms)) {
            return pjp.proceed(); // 已有权限
        } else {
            // 发起权限申请(需配合 ActivityResultLauncher)
            PermissionRequester.request(activity, perms, () -> {
                try { pjp.proceed(); } catch (Throwable ignored) {} 
            });
            return null; // 阻断原方法执行
        }
    }
    
    private boolean hasPermissions(Context ctx, String... perms) {
        for (String perm : perms) {
            if (ContextCompat.checkSelfPermission(ctx, perm) != PERMISSION_GRANTED) 
                return false;
        }
        return true;
    }
}

四、高频问题解决方案

问题 1:织入(Weaving)失效

排查步骤

  1. 检查是否应用了 AGP 7.0+ 的兼容插件

  2. 确认 aspectjrt 版本一致性

  3. 使用命令检查织入结果:

./gradlew clean assembleDebug --debug | grep "ajc"
问题 2:Lambda 表达式支持

在 build.gradle 中添加:

aspectj {
    excludeJar "com.android.support" // 排除冲突库
    weaveInfo = true
    addSerialVoid = true // 修复 Lambda 支持
}

五、性能优化建议

  1. 避免在切面中执行耗时操作
    (如:IO 读写、网络请求)

  2. 精确限定切点范围

// 劣质写法:扫描全包
@Pointcut("execution(* com.app..*.*(..))")

// 优化写法:精确到类
@Pointcut("within(com.ui.HomeActivity) || within(com.service.*Impl)")

        编译时排除第三方库

aspectj {
    exclude "androidx.*", "com.google.*" 
}

六、AspectJ vs 其他方案对比

方案集成难度性能功能完整性适用场景
AspectJ★★★☆⚡⚡⚡✅ 100%需要完整 AOP 支持
ASM★★★★☆⚡⚡⚡⚡⚠️ 手动实现高性能字节码操作
动态代理★★☆❌ 仅限接口简单运行时拦截
注解处理器★★★☆⚡⚡⚠️ 无执行期生成代码替代运行时逻辑

💡 结论:AspectJ 仍是 Android 平台功能最完备的 AOP 方案,适合复杂横切逻辑。

七、高级技巧:编译时代码注入

在 @Around 中动态修改参数:

@Around("execution(* com.payment.process(..))")
public Object encryptPayment(ProceedingJoinPoint pjp) throws Throwable {
    Object[] args = pjp.getArgs();
    if (args.length > 0 && args[0] instanceof PaymentRequest) {
        PaymentRequest request = (PaymentRequest) args[0];
        request.setCardToken(AES.encrypt(request.getCardNumber())); // 自动加密
        args[0] = request; // 替换参数
    }
    return pjp.proceed(args); // 传入新参数
}

通过本指南,你可快速掌握 AspectJ 在 Android 中的工程化应用。建议从 日志监控权限管理 等场景入手,逐步深入字节码层优化。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…

dify打造数据可视化图表

一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…

企业如何增强终端安全?

在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率&#xff0c…

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线, n r n_r nr​ 根接收天线的 MIMO 系…

Linux --进程控制

本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…