APT和AOP在android那些事情
業務背景
大家對eventbus應該不會陌生吧,eventbus是一個非常優秀的事件總線框架,在設計模式中,有點類似觀察者模式,只不過一個升級版本的觀察者模式,並且發送跟接收是切割的,解決很多讓人頭疼的內存泄漏問。其中eventbus所用到的技術就是apt,如果大家在項目中有使用到觀察者模式,不妨用eventbus來改善一下。
想象一個業務場景,業務要統計activity的啓動耗時,也就是oncreate,onstart這些生命週期方法的耗時,一種原始的方式就是在每個activity的生命週期中添加重複的代碼,比如:
@Override
protected void onCreate(Bundle savedInstanceState) {
long start=System.currentTimeMillis();
super.onCreate(savedInstanceState); //1
setContentView(R.layout.activity_main);
long spend=System.currentTimeMillis()-start; //2
}
想想一個activity那麼多生命週期,一個android項目那麼多activity,是不是很繁瑣呢。這時候aop就出場了,爲什麼不用谷歌親兒子apt呢,因爲apt一個比較大的侷限性是:無法在當前類添加刪除代碼,只能創建一個新的類。簡單點概括就是aop是一個比apt更強大的代碼生成框架。
簡介
- APT:(Annotation Processing Tool)即註解處理器,是一種註解處理的工具,用來在編譯器掃描以及處理註解。以註解作爲橋樑,通過預先設定好的代碼規則來生成對應的Java代碼
- AOP:AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程。所以這裏所代表的其實是一種思想,實現這個思想的技術有:ASPECTJ,ASM,JAVASIST,主要是這三個派系
## 對比
能力 | APT | AOP |
---|---|---|
創建新類 | y | y |
修改當前類 | n | y |
實現技術 | AutoService | ASPECTJ,ASM,JAVASIST |
註解 | y | y |
sdk代碼修改 | n | y |
android打包流程
上一張圖中,compilers階段其實是有很多個transform組成的
其中apt,aop都是作用於compilers階段apt作用於class -->.dex階段,aop作用於class-->.dex或者jar-->.dex階段。可以進一步看到aop更加強大
使用小建議
apt,aop雖然能夠減少很多重複代碼量,代碼結構優化,但是也看出來了,沒有源代碼,不利於排查問題,所以使用者一定要剋制,不是這兩個技術很黑科技就喜歡去使用,否則就是給自己的項目埋下風險。同學編寫這些apt,aop代碼的同學一定要留夠足夠的調試信息,方便後續的問題追蹤
細講apt
其實網絡上已經有很多優秀apt的案例,就不想重複的編寫這個demo.貼一個推薦的案例
apt案例
細講aop
用來動態生成類或者增強既有類的功能,ASM是可以直接產生二進制 class 文件,也可以在類被加載入 Java 虛擬機之前動態改變類行爲,底層字節碼框架,操縱的級別是底層JVM的彙編指令級別,這要求ASM使用者要對class組織結構和JVM彙編指令有一定的瞭解。
總體思路講解
- 註冊transform,接收編譯階段代碼的輸入,這裏主要有class和jar包
- 使用java的jarfile接口解析jar包,並且獲得jar包裏面的class文件
JarFile jarFile = new JarFile(inFile)
Enumeration<JarEntry> entries = jarFile.entries()
- 使用asm藉口解析jar包裏面的class,更改自己想要的代碼操作,建議使用asm5版本,gradle默認會有這個sdk,無需集成,示例:
ClassReader reader = new ClassReader(entryBytes)
ClassWriter classWriter = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
ClassVisitor visitor = new MyClassVisitor(classWriter)
- 無論更改與否jar包或者class,都要將文件拷貝到下一個輸入位置,參考示例:
File outFile = transformInvocation.outputProvider.getContentLocation(
jarInput.file.absolutePath,
jarInput.contentTypes,
jarInput.scopes,
Format.JAR)
FileUtils.copyFile(jarInput.file, outFile)
網上有一個非常好的講解,並且有示例源碼:
案例
aspectj是通過註解來實現aop的切面方案,也是最容易上手的一個方案
案例
總結
上面的講究都是引用了其他人的講解成果,後面會輸出個人編寫demo的文章,更加細緻的講解aop這塊,當然估計可能沒有我現在所引用的案例優秀,透徹。
希望我的文字能給你帶來幫助,同時也歡迎關注個人公衆號,我的所有文章第一手信息回優先發布這裏。
個人公衆號:河邊的小黑屋