【Android】一文詳解Android里的AOP編程

一文詳解Android里的AOP編程

1. 基于 AspectJ(編譯期/打包期織入)

  • 思路:用 AspectJ 編譯器在 編譯階段Gradle Transform 階段,把切面邏輯織入 class / bytecode。

  • 特點

    • 能實現類似 Spring AOP 的注解切面,支持 @Around@Before@After 等。
    • 典型應用:埋點、性能監控、日志采集。
  • 集成方式

    • 使用 Hujiang/gradle_plugin_android_aspectjx(支持 Gradle 插件織入)。

    • 在切面類里寫:

      @Aspect
      public class LogAspect {@Pointcut("execution(* com.example.myapp..*(..))")public void methodPointcut() {}@Before("methodPointcut()")public void beforeMethod(JoinPoint joinPoint) {Log.d("AOP", "調用方法: " + joinPoint.getSignature());}
      }
      

2. 基于 ASM / Javassist(字節碼修改)

  • 思路:在 編譯后 / 打包前 修改字節碼,插入所需邏輯。
  • 特點
    • 更底層、更靈活,但開發成本高。
    • 一般用于統一插樁:如所有 setImageBitmap() 加水印。
  • 實現方式
    • 自定義 Transform,用 ASMJavassist 遍歷 class 文件并修改。
    • 常見框架:ByteX、booster、ASM 手寫工具。

3. 基于 動態代理 / 反射(運行時 AOP)

  • 思路:利用 Java 動態代理CGLIB(在 Android 上不常用) 在運行時生成代理對象。

  • 限制

    • 只能代理 接口(因為 JDK 動態代理只能代理接口方法)。
    • 對 Android 性能有一定影響(尤其是頻繁調用)。
  • 適用場景

    • JSBridge、接口統一攔截、埋點 SDK。
  • 示例

    public class ProxyHandler implements InvocationHandler {private final Object target;public ProxyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Log.d("AOP", "調用前: " + method.getName());Object result = method.invoke(target, args);Log.d("AOP", "調用后: " + method.getName());return result;}
    }// 使用
    MyInterface proxy = (MyInterface) Proxy.newProxyInstance(MyInterface.class.getClassLoader(),new Class[]{MyInterface.class},new ProxyHandler(new MyInterfaceImpl())
    );
    

4. 基于 Hook / 插樁框架

  • 思路:通過系統 Hook 框架或 Xposed,對方法進行攔截。
  • 適用場景:逆向分析、黑科技應用,或者企業內的監控 SDK。
  • 常用框架
    • Xposed / EdXposed
    • Epic(美團的運行時 Hook 框架,支持 Android ART)
    • SandHook 等。

5. 對比與建議

方案時機優點缺點適合場景
AspectJ編譯/打包期寫法優雅,貼近 Spring AOP編譯速度慢,Gradle 配置復雜日志埋點、性能監控
ASM/Javassist編譯/打包期靈活,性能開銷低學習成本高全局插樁、修改框架方法
動態代理運行時實現快,適合接口只能代理接口,性能差SDK 接口攔截、調試工具
Xposed/Epic運行時功能強大需 root/侵入性強第三方 Hook、逆向

寫一個 Android 上 AspectJ AOP Demo,實現 攔截所有 View.OnClickListener 的點擊事件,并做點擊埋點(比如輸出日志)。

這個 Demo 分三部分:

  1. Gradle 配置 AspectJ 插件
  2. 定義切面類 @Aspect
  3. 測試按鈕點擊是否被攔截

6. Gradle 配置

首先在 app/build.gradle 里加上 AspectJX 插件(常用的開源實現是 Hujiang AspectJX):

plugins {id 'com.android.application'id 'android-aspectjx' // 加上這一行
}android {namespace "com.example.aopdemo"compileSdk 34defaultConfig {applicationId "com.example.aopdemo"minSdk 24targetSdk 34versionCode 1versionName "1.0"}
}dependencies {implementation 'org.aspectj:aspectjrt:1.9.7'
}

根目錄 build.gradle 里要加上插件 classpath:

buildscript {dependencies {classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'}
}

7. 定義切面類

app/src/main/java/com/example/aopdemo/aspect/ClickAspect.java 里寫一個切面類:

package com.example.aopdemo.aspect;import android.util.Log;
import android.view.View;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;@Aspect
public class ClickAspect {private static final String TAG = "AOP_CLICK";/*** 定義切點:攔截所有 View.OnClickListener 的 onClick(View) 方法*/@Pointcut("execution(* android.view.View.OnClickListener.onClick(..))")public void onClickMethod() {}/*** 環繞通知:在點擊前后都能插入邏輯*/@Around("onClickMethod()")public void aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {// 獲取參數(點擊的 View)Object[] args = joinPoint.getArgs();if (args != null && args.length > 0 && args[0] instanceof View) {View view = (View) args[0];int id = view.getId();String viewName = view.getResources().getResourceEntryName(id);Log.d(TAG, "點擊事件埋點: viewId=" + id + " viewName=" + viewName);}// 執行原始方法(必須,不然點擊邏輯會被攔截掉)joinPoint.proceed();}
}

8. Activity 測試

MainActivity.java 里隨便放個按鈕:

package com.example.aopdemo;import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button btn = findViewById(R.id.btn_test);btn.setOnClickListener(v -> Log.d(TAG, "按鈕邏輯被執行"));}
}

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:gravity="center"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/btn_test"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="點我試試"/>
</LinearLayout>

9. 運行效果

點擊按鈕時,Logcat 會輸出兩條日志:

D/AOP_CLICK: 點擊事件埋點: viewId=2131230890 viewName=btn_test
D/MainActivity: 按鈕邏輯被執行

? 這樣就實現了 全局點擊埋點 AOP
后續可以把 Log.d 換成上報到埋點 SDK、神策、Firebase 等。

10. 原理解析

AspectJ 里,ProceedingJoinPoint 是一個 連接點(JoinPoint)的運行時對象,它代表了當前被攔截的方法調用。


10. 1 它的來源

  • ProceedingJoinPoint 繼承自 JoinPoint,專門用于 @Around 環繞通知。
  • JoinPoint 本身只能“看”,不能“改”;而 ProceedingJoinPoint 可以“執行原方法”,即調用 proceed()

10.2 它能拿到什么

@Around 方法里可以通過它獲取很多信息:

方法說明示例
joinPoint.getArgs()獲取目標方法的參數數組[View v]
joinPoint.getTarget()獲取被代理對象(目標對象)某個 OnClickListener 實例
joinPoint.getThis()獲取代理對象(AOP 生成的代理類)代理后的對象
joinPoint.getSignature()獲取方法簽名void onClick(View)
joinPoint.getKind()獲取連接點類型method-execution
joinPoint.getSourceLocation()獲取源碼位置(類名、行號)MainActivity$1.onClick(MainActivity.java:27)

10.3 最重要的 proceed()

  • joinPoint.proceed():執行原始方法(帶上原始參數)。
  • joinPoint.proceed(Object[] args):可以 修改參數后再執行

比如我們在點擊埋點的時候,也可以偷偷改掉參數:

@Around("onClickMethod()")
public void aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {Object[] args = joinPoint.getArgs();if (args != null && args.length > 0 && args[0] instanceof View) {View view = (View) args[0];Log.d("AOP_CLICK", "點擊: " + view.getId());}// 執行原方法(必須,不然點擊邏輯不會繼續)joinPoint.proceed(args);
}

10.4 在點擊埋點場景里

  • joinPoint.getTarget() 👉 實際上就是 View.OnClickListener 對象
  • joinPoint.getArgs()[0] 👉 傳入的 View v
  • joinPoint.proceed() 👉 真正調用 listener.onClick(v)

📌 總結一句:
ProceedingJoinPoint = 運行時對當前方法調用的描述 + 能控制是否繼續執行原方法。


畫一個調用流程圖,展示 點擊按鈕 → AOP 切面 → proceed() → 原始 onClick 方法 的執行路徑。


用戶點擊按鈕
系統觸發 View.OnClickListener.onClick()
進入 AOP 切面 @Around 通知
執行切面邏輯(埋點/日志/統計)
調用 joinPoint.proceed()?
攔截掉,不執行原始邏輯
執行原始 onClick 方法
按鈕原有邏輯被執行
流程結束

🔍 解釋

  1. 用戶點按鈕 → 系統調用 OnClickListener.onClick()
  2. 因為我們在方法上織入了 AOP → 先進入切面(@Around
  3. 切面邏輯(埋點、打印日志、防抖動判斷…)
  4. joinPoint.proceed() 決定是否繼續調用原始方法:
    • 調用了:進入原始 onClick 邏輯
    • 不調用:事件被“吃掉”,原始邏輯不會執行

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/93876.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/93876.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/93876.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AI+預測3D新模型百十個定位預測+膽碼預測+去和尾2025年8月21日第167彈

從今天開始&#xff0c;咱們還是暫時基于舊的模型進行預測&#xff0c;好了&#xff0c;廢話不多說&#xff0c;按照老辦法&#xff0c;重點8-9碼定位&#xff0c;配合三膽下1或下2&#xff0c;殺1-2個和尾&#xff0c;再殺4-5個和值&#xff0c;可以做到100-300注左右。(1)定位…

機器學習【十】neural network

系統梳理了機器學習與神經網絡的基礎知識&#xff0c;涵蓋理論、核心概念及代碼實踐。理論部分包括線性模型&#xff08;向量表示、廣義線性模型&#xff09;、分類與回歸的區別、梯度下降&#xff08;批量/隨機/小批量&#xff09;、激活函數&#xff08;Sigmoid、ReLU等&…

如何用算力魔方4060安裝PaddleOCR MCP 服務器

在當今數字化快速發展的時代&#xff0c;OCR&#xff08;光學字符識別&#xff09;技術已經成為從圖像中提取文本信息的重要工具。無論是在自動化辦公、智能文檔處理還是在內容創作領域&#xff0c;OCR 技術的應用都極大地提高了工作效率和準確性。本文將詳細介紹如何利用算力魔…

Azure的遷移專業服務是怎么提供的

好的&#xff0c;這是一個非常實際的問題。Azure的遷移專業服務&#xff08;Professional Services for Migration&#xff09;并非一個單一的“產品”&#xff0c;而是一個由微軟及其龐大的合作伙伴生態系統共同提供的、基于成熟方法論的綜合服務框架。其提供方式可以概括為&a…

Seaborn數據可視化實戰:Seaborn入門-環境搭建與基礎操作

Seaborn環境搭建與配置 學習目標 本課程將指導學員如何在不同的操作系統&#xff08;Windows, macOS, Linux&#xff09;上安裝Seaborn庫&#xff0c;以及如何配置Python環境&#xff0c;包括使用Jupyter Notebook和Spyder等集成開發環境&#xff08;IDE&#xff09;的基本操作…

Windows下RabbitMQ完整安裝指南

一、RabbitMQ 簡介 RabbitMQ 是一款基于 Erlang 語言開發的開源消息隊列中間件&#xff0c;實現了高級消息隊列協議&#xff08;AMQP&#xff09;。其最初起源于金融系統&#xff0c;專為分布式系統中的消息存儲與轉發設計&#xff0c;在可靠性、擴展性和高可用性方面表現卓越…

thingsboard 通過Entities hierarchy部件實現左邊菜單點擊,右邊的表格按左邊的分類型進行過濾篩選數據源

在 ThingsBoard 中&#xff0c;要讓“Entities hierarchy”部件&#xff08;左側樹形導航&#xff09;與右側的數據表格實現聯動——即點擊左側某個節點后&#xff0c;右側表格立刻按該節點對應的實體類型/層級進行過濾——需要把“數據源別名&#xff08;Alias&#xff09; 儀…

【Ansible】核心概念解析:架構、清單管理與配置入門

本專欄文章持續更新&#xff0c;新增內容使用藍色表示。對于系統管理員而言&#xff0c;手動管理每一臺服務器不僅維護難度極大&#xff0c;而且即使經驗豐富&#xff0c;也難免出現疏忽和錯誤。自動化技術能有效避免因手動管理系統和基礎架構而產生的各類問題。其優點包括&…

rs-fMRI_兩篇文章中分析方法的梳理(近乎翻譯)

文章一文章信息APOE ε4 influences within and between network functional connectivity in posterior cortical atrophy and logopenic progressive aphasia2024美國梅奧診所發表在Alzheimers Dement. 的文章。“APOE ε4等位基因對后皮質萎縮與進行性語言障礙型失語癥的網絡…

在互聯網大廠的Java面試:謝飛機的搞笑歷險記

在互聯網大廠的Java面試&#xff1a;謝飛機的搞笑歷險記 在一個陽光明媚的早上&#xff0c;我們的主角&#xff0c;程序員謝飛機&#xff0c;走進了一家著名的互聯網大廠&#xff0c;準備迎接他人生中最嚴峻的挑戰——Java面試。 第一輪&#xff1a;基礎技術面試 面試官&#x…

微軟AD國產化替換倒計時——不是選擇題,而是生存題

一直以來&#xff0c;微軟Active Directory&#xff08;AD&#xff09;作為企業身份管理和訪問控制的核心組件&#xff0c;承擔著用戶認證、權限分配、資源目錄管理等基礎職能。然而&#xff0c;隨著政策、合規與網絡安全壓力不斷加劇&#xff0c;AD面臨著前所未有的挑戰&#…

MyBatis-Plus MetaObjectHandler的幾個坑(主要是id字段)

1.背景 主要是要實現一個id字段的自增長&#xff0c;不依賴數據庫的能力&#xff08;已避免后續換庫的問題&#xff09;。姑且使用redis作為表的id分配器&#xff0c;因此使用MyBatis-Plus MetaObjectHandler對每個insert的id進行分配。 2.實施過程 以下是實現過程 1.實現MetaO…

Springboot 項目配置多數據源

Springboot 項目配置多數據源 基礎環境 java8、springboot2.2.13、mybatis、mysql5.x、oracle 項目配置 1.application.yml spring:datasource:mysql1:username: abcpassword: 123456url: jdbc:mysql://127.0.0.1:3306/panda?useUnicodetrue&characterEncodingUTF-8&z…

STM32_0001 KEILMDK V5.36 編譯一個STM32F103C8T6說core_cm3.h文件找不到以及編譯器版本不匹配的解決辦法

KEILMDK V5.36 編譯一個STM32F103C8T6說core_cm3.h文件找不到的解決辦法利用KEILMDK V5.36 編譯一個STM32F103C8T6說core_cm3.h文件找不到。主要錯誤信息如下D:/stm32studio/Armmdk/Packs/Keil/STM32F1xx_DFP/2.4.1/Device/Include\stm32f10x.h(486): error: core_cm3.h file n…

基于Transformer的機器翻譯——訓練篇

前言 還在為機器翻譯模型從理論到落地卡殼&#xff1f;系列博客第三彈——模型訓練篇強勢登場&#xff0c;手把手帶你走完Transformer中日翻譯項目的最后關鍵一步&#xff01; 前兩期我們搞定了數據預處理&#xff08;分詞、詞表構建全流程&#xff09;和模型搭建&#xff08…

智能編程中的智能體與 AI 應用:概念、架構與實踐場景

一、智能體&#xff08;Intelligent Agent&#xff09;在編程中的定義與架構1. 智能體的核心概念 智能體是指在特定環境中能夠自主感知、決策并執行動作的軟件實體&#xff0c;具備以下特征&#xff1a;自主性&#xff1a;無需人工干預即可根據環境變化調整行為。交互性&#x…

數組實現各類數據結構

目錄 一、數組實現單鏈表 二、數組實現雙鏈表 三、數組實現棧 四、數組模擬隊列 五、數組模擬單調棧 六、數組模擬單調隊列&#xff08;滑動窗口&#xff09; 七、數組模擬堆 一、數組實現單鏈表 #include<iostream> #include<algorithm> #include<cstr…

數據處理與統計分析 —— apply自定義函數

目錄 一、向量化與偽向量化 1、向量化 2、np.vectorize 偽向量化&#xff08;特定場景&#xff09; 3、apply&#xff08;自定義函數&#xff09; 二、apply函數 1、對series中使用apply 2、對dataframe中使用apply 3、apply函數案例-泰坦尼克號數據集] 數據集下載鏈接&#xf…

如何有效利用大語言模型來智能加速產業聯盟的產業鏈轉化路徑?

觀點作者&#xff1a;科易網AI技術轉移研究院在科技創新浪潮席卷全球的今天&#xff0c;科技成果轉化已成為衡量一個國家創新能力的重要標志。然而&#xff0c;一項權威調查顯示&#xff0c;我國科技成果轉化率不足30%&#xff0c;大量有價值的創新成果仍停留在實驗室階段&…

視頻加水印 視頻加水印軟件 視頻加動態水印

如果你有一個視頻&#xff0c;你想給他加一個水印&#xff0c;那么你可以使用這個工具&#xff0c;準備好你的視頻和水印。水印一般采用PNG&#xff0c;打開這個工具&#xff0c;把你的視頻和水印拖進這個方框當中。視頻限制是MP4&#xff0c;水印限制是PNG&#xff0c;它可以把…