Android中的AOP
什麼是AOP
AOP(Aspect Oriented Program的首字母縮寫)是一種面向切面程式設計的思想。這種程式設計思想是相對於OOP(ObjectOriented Programming即物件導向程式設計)來說的。
先來說一下大家熟悉的物件導向程式設計:物件導向的特點是繼承、多型和封裝。而封裝就要求將功能分散到不同的物件中去,這在軟體設計中往往稱為職責分配。實際上也就是說,讓不同的類設計不同的方法。這樣程式碼就分散到一個個的類中去了。這樣做的好處是降低了程式碼的複雜程度,使類可重用。但是物件導向的程式設計天生有個缺點就是分散程式碼的同時,也增加了程式碼的重複性。
比如我希望在專案裡面所有的模組都增加日誌統計模組,按照OOP的思想,我們需要在各個模組裡面都新增統計程式碼,但是如果按照AOP的思想,可以將統計的地方抽象成切面,只需要在切面裡面新增統計程式碼就OK了。
其實在服務端的領域AOP已經被玩的風生水起,例如Spring的框架。
程式碼demo連結 github.com/xsfelvis/an…
1、APT + JavaPoet
APT(Annotation Processing Tool 的簡稱),可以在程式碼編譯期解析註解,結合JavaPoet 生成新的 Java 檔案,減少手動的程式碼輸入。現在有很多主流庫都用上了 APT,比如 Dagger2, ButterKnife, EventBus3 等
更多可以參考編譯時註解之APT
1、定義註解
例如
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
複製程式碼
2、定義Processor派生自AbstractProcessor 並且使用@AutoService標註
AutoService會自動在META-INF資料夾下生成Processor配置資訊檔案,該檔案裡就是實現該服務介面的具體實現類。而當外部程式裝配這個模組的時候,
就能通過該jar包META-INF/services/裡的配置檔案找到具體的實現類名,並裝載例項化,完成模組的注入
3、提取註解資訊
在Processor中提取註解資訊 結合一些手段去處理,比如JavaPoet,或者 JavaPoet使用指南
核心點
- 處理Processor中的 註解資訊的套路
- javapoet中的程式碼生成 Elements
2、AspectJ
簡單介紹
- AspectJ 是使用最為廣泛的 AOP 實現方案,適用於 Java 平臺,官網地址:www.eclipse.org/aspectj/ 其中AspectJ 是在靜態織入程式碼,即在編譯期注入程式碼的。
- AspectJ 提供了一套全新的語法實現,完全相容 Java(跟 Java 之間的區別,只是多了一些關鍵詞而已)。同時,還提供了純 Java 語言的實現,通過註解的方式,完成程式碼編織的功能。因此我們在使用 AspectJ 的時候有以下兩種方式:
- 使用 AspectJ 的語言進行開發
- 通過 AspectJ 提供的註解在 Java 語言上開發
- 使用 AspectJ 的語言進行開發
- 因為最終的目的其實都是需要在位元組碼檔案中織入我們自己定義的切面程式碼,不管使用哪種方式接入 AspectJ,都需要使用 AspectJ 提供的程式碼編譯工具 ajc 進行編譯。
- 在 Android Studio 上一般使用註解的方式使用 AspectJ,因為 Android Studio 沒有 AspectJ 外掛,無法識別 AspectJ 的語法(不過在 Intellij IDEA 收費版上可以使用 AspectJ 外掛),所以後面的語法說明和示例都是以註解的實現方式
術語
- JoinPoints(連線點)
JoinPoints(連線點),程式中可能作為程式碼注入目標的特定的點。在AspectJ中可以作為JoinPoints的地方包括
- PointCuts(切入點)
PointCuts(切入點),其實就是程式碼注入的位置。與前面的JoinPoints不同的地方在於,其實PointCuts是有條件限定的JoinPoints。比如說,在一個Java原始檔中,會有很多的JoinPoints,但是我們只希望對其中帶有@debug註解的地方才注入程式碼。所以,PointCuts是通過語法標準給JoinPoints新增了篩選條件限定
- Advice(通知)
Advice(通知),其實就是注入到class檔案中的程式碼片。典型的 Advice 型別有 before、after 和 around,分別表示在目標方法執行之前、執行後和完全替代目標方法執行的程式碼
- Aspect(切面)
Aspect(切面),Pointcut 和 Advice 的組合看做切面。
- Weaving
注入程式碼(advices)到目標位置(joint points)的過程
3、Javassit
簡介
Javassist作用是在編譯器間修改class檔案,與之相似的ASM(熱修復框架女媧)也有這個功能,可以讓我們直接修改編譯後的class二進位制程式碼,
首先我們得知道什麼時候編譯完成,並且我們要趕在class檔案被轉化為dex檔案之前去修改。在Transfrom這個api出來之前,想要在專案被打包成dex之前對class進行操作,必須自定義一個Task,然後插入到predex或者dex之前,在自定義的Task中可以使用javassist或者asm對class進行操作。而Transform則更為方便,Transfrom會有他自己的執行時機,不需要我們插入到某個Task前面。Tranfrom一經註冊便會自動新增到Task執行序列中,並且正好是專案被打包成dex之前。
常用方式
需要配合自定義 GradlePlugin、TransForm,往往在TransForm期間使用Javassit 去完成一些需要的aop,關於自定義Gradle外掛這裡就不繼續展開了
ClassPool、CtClass、CtMethod核心類的使用在這裡展示的很詳細。
1、初始化ClassPool設定 2、通過包名取到對應的CtClass 3、CtMethodi插入程式碼塊,寫檔案,釋放,結束整個注入程式碼過程。
結語
這幾種AOP方式各自最大的特點就是編織程式碼的時機不同,這個需要在使用的時候根據實際需要來,
上圖中 javaassit和asm也可以結合apt使用的,比如建立類,不過目前建立類一般都是javapoet,它們只是工具而已 通常aop可以用來處理以下問題
- 日誌、監控
- 登陸
- 修復第三方程式碼,比如第三方jar包
- hook 某些程式碼為封裝過的安全性更好的程式碼, 比如hook系統toast或者Gson等等
- ……