在Android中使用AspectJ的簡易步驟
最近有做使用者行為統計的需求,為了儘可能使統計程式碼不侵入業務程式碼,就研究了下hook和Aop。
之前寫的hook方面的文章裡,有評論給出了些建議,於是研究了下AspectJ,雖然還是不能完美解決專案中的問題,不過確實是個好東西。
實踐了一把,這裡簡單記錄一下。
先來一堆參考連結
ofollow,noindex">【翻譯】Android中的AOP程式設計
Android Studio 中自定義 Gradle 外掛
jarryleo / MagicBuriedPoint言歸正傳
1.新建一個Library的module
新建一個module,型別選Library。
比如這裡的TrackPoint

20180930103904.png
切面程式碼的定義基本就寫在這裡。
2.新增依賴
我們這種凡夫俗子要做點事只能抱一下大神的大腿,我們這裡用一下別人寫好的SDK: https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx
2.1.根目錄的build.gradle裡
buildscript { ... dependencies { ... classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0' } }
2.2.app專案的build.gradle及新建的module的build.gradle裡都應用外掛
apply plugin: 'android-aspectjx'
2.3.在app的build.gradle裡面新增剛才新建的那個庫
dependencies { ... implementation project(':TrackPoint') }
3.定義切入點
Library裡定義檔案主要分三個檔案TrackPointCallBack,TrackPoint,TrackPointAspect
3.1.先定義介面以供呼叫
TrackPointCallBack和TrackPoint兩個檔案就是介面和呼叫方法定義,簡單起見,這裡僅以點選和頁面的開啟關閉為例:
package net.codepig.trackpoint; public class TrackPoint { private static TrackPointCallBack trackPointCallBack; private TrackPoint() { } public static void init(TrackPointCallBack callBack) { trackPointCallBack = callBack; } static void onClick(String pageClassName, String viewIdName) { if (trackPointCallBack == null) { return; } trackPointCallBack.onClick(pageClassName, viewIdName); } static void onPageOpen(String pageClassName) { if (trackPointCallBack == null) { return; } trackPointCallBack.onPageOpen(pageClassName); } static void onPageClose(String pageClassName) { if (trackPointCallBack == null) { return; } trackPointCallBack.onPageClose(pageClassName); } }
package net.codepig.trackpoint; public interface TrackPointCallBack { void onClick(String pageClassName, String viewIdName); void onPageOpen(String pageClassName); void onPageClose(String pageClassName); }
3.2.Aspect的定義
這裡是重點,就靠這個在不涉及業務程式碼的情況下,在需要的事件前後插入新的行為。
先看程式碼:
package net.codepig.trackpoint; 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 TrackPointAspect { @Pointcut("execution(* onClick(..))") public void methodPointcut() { } @Pointcut("execution(* android.app.Activity+.onCreate(..))") public void activityOnCreatePointcut() { } @Pointcut("execution(* android.app.Activity+.onDestroy(..))") public void activityOnDestroyPointcut() { } @Pointcut("execution(* android.app.Fragment+.onCreate(..))") public void fragmentOnCreatePointcut() { } @Pointcut("execution(* android.support.v4.app.Fragment+.onCreate(..))") public void fragmentV4OnCreatePointcut() { } @Pointcut("execution(* android.app.Fragment+.onDestroy(..))") public void fragmentOnDestroyPointcut() { } @Pointcut("execution(* android.support.v4.app.Fragment+.onDestroy(..))") public void fragmentV4OnDestroyPointcut() { } @Around("onClickPointcut()") public void aroundJoinClickPoint(final ProceedingJoinPoint joinPoint) throws Throwable { Object target = joinPoint.getTarget(); String className = ""; if (target != null) { className = target.getClass().getName(); } //獲取點選事件view物件及名稱,可以對不同按鈕的點選事件進行統計 Object[] args = joinPoint.getArgs(); if (args.length >= 1 && args[0] instanceof View) { View view = (View) args[0]; int id = view.getId(); String entryName = view.getResources().getResourceEntryName(id); TrackPoint.onClick(className, entryName); } joinPoint.proceed();//執行原來的程式碼 } @Around("activityOnCreatePointcut() || fragmentOnCreatePointcut() || fragmentV4OnCreatePointcut()") public void aroundJoinPageOpenPoint(final ProceedingJoinPoint joinPoint) throws Throwable { Object target = joinPoint.getTarget(); String className = target.getClass().getName(); TrackPoint.onPageOpen(className); joinPoint.proceed(); } @Around("activityOnDestroyPointcut() || fragmentOnDestroyPointcut() || fragmentV4OnDestroyPointcut()") public void aroundJoinPageClosePoint(final ProceedingJoinPoint joinPoint) throws Throwable { Object target = joinPoint.getTarget(); String className = target.getClass().getName(); TrackPoint.onPageClose(className); joinPoint.proceed(); } }
具體的語法可以參考前面列出的參考教程,這裡只是簡單的做一下說明:
3.2.1 @Pointcut
語法
設定需要切入的方法。其中引數中第一個*表示返回值使用任意型別。方法中的(..)也表示使用任意型別。
需要注意的是這裡的Fragment相關需要加上 android.support
類的支援。
3.2.2 @Around
, @Before
, @After
語法
定義具體插入的程式碼,比如在相關的事件上插入需要的統計程式碼。
這裡可以使用『&&、||、!』來組合不同的Pointcut定義。比如 @Around("activityOnDestroyPointcut() || fragmentOnDestroyPointcut() || fragmentV4OnDestroyPointcut()")
就是組合了三種關閉事件。
4.初始化
初始化可以在 Application
中進行。(沒有的話就建一個。)
然後在onCreate方法中進行初始化
public class MainApp extends Application { private static final String TAG = "LOGCAT"; @Override public void onCreate() { super.onCreate(); TrackPoint.init(new TrackPointCallBack() { @Override public void onClick(String pageClassName, String viewIdName) { Log.d(TAG, "onClick: " + pageClassName + "-" + viewIdName); //新增你的操作 } @Override public void onPageOpen(String pageClassName) { Log.d(TAG, "onPageOpen: " + pageClassName); //新增你的操作 } @Override public void onPageClose(String pageClassName) { Log.d(TAG, "onPageClose: " + pageClassName); //新增你的操作 } }); } }
收工!
當然值得操作的事件肯定不止上面這三種,以後慢慢新增吧。
相關github專案地址: https://github.com/codeqian/aspectJDemo