AOP(Aspect-Oriented Programming),即是面向切面編程,AspectJ是實現AOP的一個重要的框架,它是使用AspectJ編譯器(ajc),在編譯時期,在關鍵的的地方插入部分代碼,處理相關邏輯,比如可以用於打印方法執行的效率,權限檢查等。在Android上的應用主要是做性能監控、基於註解的數據埋點等,Hugo 就是基於AspectJ實現。
教程
修改項目根目錄的 build.gradle 文件
buildscript {
dependencies {
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
}
}
在APP和Module的build.gradle增加插件
apply plugin: 'com.android.application'
apply plugin: 'android-aspectjx'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// 下面這行不是必須的,但是爲了有時候去掉上面插件不報錯就需要增加
implementation 'org.aspectj:aspectjrt:1.9.4'
}
創建註解
用於打印日誌
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AspectDebugLog {
}
用於埋點
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AspectAnalyze {
String name();
}
創建配置
@SuppressWarnings("unused")
@Aspect
public class AspectTrace {
private static AspectTraceListener aspectTraceListener;
/**
* 針對所有繼承 Activity 類的 onCreate 方法
*/
@Pointcut("execution(* android.app.Activity+.onCreate(..))")
public void activityOnCreatePointcut() {
}
/**
* 針對帶有AspectAnalyze註解的方法
*/
@Pointcut("execution(@com.taoweiji.aspect.trace.AspectAnalyze * *(..))")
public void aspectAnalyzeAnnotation() {
}
/**
* 針對帶有AspectDebugLog註解的方法
*/
@Pointcut("execution(@com.taoweiji.aspect.trace.AspectDebugLog * *(..))")
public void aspectDebugLogAnnotation() {
}
/**
* 針對前面 aspectAnalyzeAnnotation() 的配置
*/
@Around("aspectAnalyzeAnnotation()")
public void aroundJoinAspectAnalyze(final ProceedingJoinPoint joinPoint) throws Throwable {
Object target = joinPoint.getTarget();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
AspectAnalyze aspectAnalyze = methodSignature.getMethod().getAnnotation(AspectAnalyze.class);
long startTimeMillis = System.currentTimeMillis();
joinPoint.proceed();
if (aspectTraceListener != null) {
aspectTraceListener.onAspectAnalyze(joinPoint, aspectAnalyze, methodSignature, System.currentTimeMillis() - startTimeMillis);
}
}
/**
* 針對前面 aspectDebugLogAnnotation() 或 activityOnCreatePointcut() 的配置
*/
@Around("aspectDebugLogAnnotation() || activityOnCreatePointcut()")
public void aroundJoinAspectDebugLog(final ProceedingJoinPoint joinPoint) throws Throwable {
long startTimeMillis = System.currentTimeMillis();
joinPoint.proceed();
long duration = System.currentTimeMillis() - startTimeMillis;
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
SourceLocation location = joinPoint.getSourceLocation();
String message = String.format("%s(%s:%s) [%sms]", methodSignature.getMethod().getName(), location.getFileName(), location.getLine(), duration);
if (aspectTraceListener != null) {
aspectTraceListener.logger("AspectTrace", message);
} else {
Log.e("AspectTrace", message);
}
}
public static void setAspectTraceListener(AspectTraceListener aspectTraceListener) {
AspectTrace.aspectTraceListener = aspectTraceListener;
}
public interface AspectTraceListener {
void logger(String tag, String message);
void onAspectAnalyze(ProceedingJoinPoint joinPoint, AspectAnalyze aspectAnalyze, MethodSignature methodSignature, long duration);
}
}
配置
在 MyApplication 設置監聽器
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
AspectTrace.setAspectTraceListener(new AspectTrace.AspectTraceListener() {
@Override
public void logger(String tag, String message) {
Log.e(tag, message);
}
@Override
public void onAspectAnalyze(ProceedingJoinPoint joinPoint, AspectAnalyze aspectAnalyze, MethodSignature methodSignature, long duration) {
Log.e("onAspectAnalyze", aspectAnalyze.name());
// TODO 實現統計功能
}
});
}
}
public class MainActivity extends Activity {
@AspectAnalyze(name = "MainActivity.onCreate")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.name).setOnClickListener(v -> onNameClick());
}
@AspectDebugLog
@AspectAnalyze(name = "onNameClick")
public void onNameClick() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@AspectAnalyze(name = "MainActivity.onDestroy")
@Override
protected void onDestroy() {
super.onDestroy();
}
}
結果
E/onAspectAnalyze: MainActivity.onCreate
E/AspectTrace: onNameClick(MainActivity.java:28) [502ms]
E/onAspectAnalyze: onNameClick
E/onAspectAnalyze: MainActivity.onDestroy
複製代碼
基本原理分析
app/build/intermediates/ajx/debug/includefiles/com/taoweiji/aspectjexample/MainActivity.class
可以看到通過AspectJ的ajc編譯器轉換後的class,在生成dex文件之前,在.class文件中插入了一些代碼,從而實現AOP技術。
public class MainActivity extends Activity {
public MainActivity() {
}
@AspectAnalyze(
name = "MainActivity.onCreate"
)
protected void onCreate(Bundle savedInstanceState) {
JoinPoint var3 = Factory.makeJP(ajc$tjp_0, this, this, savedInstanceState);
AspectTrace var10000 = AspectTrace.aspectOf();
Object[] var5 = new Object[]{this, savedInstanceState, var3};
var10000.aroundJoinAspectAnalyze((new MainActivity$AjcClosure3(var5)).linkClosureAndJoinPoint(69648));
}
@AspectDebugLog
@AspectAnalyze(
name = "onNameClick"
)
public void onNameClick() {
JoinPoint var2 = Factory.makeJP(ajc$tjp_1, this, this);
AspectTrace var10000 = AspectTrace.aspectOf();
Object[] var4 = new Object[]{this, var2};
var10000.aroundJoinAspectAnalyze((new MainActivity$AjcClosure7(var4)).linkClosureAndJoinPoint(69648));
}
@AspectAnalyze(
name = "MainActivity.onDestroy"
)
protected void onDestroy() {
JoinPoint var1 = Factory.makeJP(ajc$tjp_2, this, this);
AspectTrace var10000 = AspectTrace.aspectOf();
Object[] var2 = new Object[]{this, var1};
var10000.aroundJoinAspectAnalyze((new MainActivity$AjcClosure9(var2)).linkClosureAndJoinPoint(69648));
}
static {
ajc$preClinit();
}
}
最後
如果你看到了這裏,覺得文章寫得不錯就給個讚唄!歡迎大家評論討論!如果你覺得那裏值得改進的,請給我留言。一定會認真查詢,修正不足,定期免費分享技術乾貨。感興趣的小夥伴可以點一下關注哦。謝謝!