個人博客 "http://www.milovetingting.cn" Android埋點方案的簡單實現 AOP之AspectJ AOP的定義 AOP為Aspect Oriented Programming的縮寫,意為:面向切麵編程,通過預編譯方式和運行期間動態代理實現程式功能的統一維護的一種技術。 ...
個人博客
Android埋點方案的簡單實現-AOP之AspectJ
AOP的定義
AOP為Aspect Oriented Programming的縮寫,意為:面向切麵編程,通過預編譯方式和運行期間動態代理實現程式功能的統一維護的一種技術。
以上關於AOP的定義引用自百度百科。
AOP的運用場景
日誌記錄、性能統計、許可權控制、埋點等
AOP的具體實現方案有很多,這裡選用AspectJ來簡單實現
- 監聽View的點擊、頁面打開、關閉
- 為方法添加開始、結束的日誌
- 統計方法運行時間
AspectJ的使用
AspectJ的引入
這裡引用AspectJX,AspectJX是基於AspectJ的一個AOP框架
新建Android工程,在項目根目錄下的build.gradle文件中添加依賴
dependencies {
//...
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
//...
}
新建Module,類型選擇Android Library,在新建的library的build.gradle文件中,添加相應的依賴
apply plugin: 'android-aspectjx'
在app的build.gradle文件中增加對剛纔新建的library的引用及AspectJ的依賴
apply plugin: 'android-aspectjx'
dependencies {
//...
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
監聽View的點擊、頁面打開、關閉
在library中新建回調介面TrackCallBack
public interface TrackCallBack {
/**
* 當View被點擊
*
* @param pageName
* @param viewIdName
*/
void onClick(String pageName, String viewIdName);
/**
* 當頁面打開時
*
* @param pageName
*/
void onPageOpen(String pageName);
/**
* 當頁面關閉時
*
* @param pageName
*/
void onPageClose(String pageName);
}
在library中新建切入點TrackPoint
public class TrackPoint {
private static TrackCallBack mTrackCallBack;
private TrackPoint() {
}
/**
* 初始化
* @param trackCallBack
*/
public static void init(TrackCallBack trackCallBack) {
mTrackCallBack = trackCallBack;
}
static void onClick(String pageName, String viewIdName) {
if (mTrackCallBack == null) {
return;
}
mTrackCallBack.onClick(pageName, viewIdName);
}
static void onPageOpen(String pageName) {
if (mTrackCallBack == null) {
return;
}
mTrackCallBack.onPageOpen(pageName);
}
static void onPageClose(String pageName) {
if (mTrackCallBack == null) {
return;
}
mTrackCallBack.onPageClose(pageName);
}
}
在library中新建切麵TraceAspect
@Aspect
public class TraceAspect {
private static final String TAG = TraceAspect.class.getSimpleName();
@Pointcut("execution(* onClick(..))")
public void onClickPointcut() {
}
@Pointcut("execution(* android.app.Activity+.onCreate(..))")
public void activityOnCreatePointcut() {
}
@Pointcut("execution(* android.app.Activity+.onDestroy(..))")
public void activityDestroyPointcut() {
}
@Around("onClickPointcut()")
public void onClick(ProceedingJoinPoint joinPoint) throws Throwable {
Object target = joinPoint.getTarget();
String className = "";
if (target != null) {
className = target.getClass().getName();
}
Object[] args = joinPoint.getArgs();
if (args.length > 0 && args[0] instanceof View) {
View view = (View) args[0];
String entryName = view.getResources().getResourceEntryName(view.getId());
TrackPoint.onClick(className, entryName);
}
joinPoint.proceed();
}
@Around("activityOnCreatePointcut()")
public void pageOpen(ProceedingJoinPoint joinPoint) throws Throwable {
Object target = joinPoint.getTarget();
String className = target.getClass().getName();
TrackPoint.onPageOpen(className);
joinPoint.proceed();
}
@Around("activityDestroyPointcut()")
public void pageClose(ProceedingJoinPoint joinPoint) throws Throwable {
Object target = joinPoint.getTarget();
String className = target.getClass().getName();
TrackPoint.onPageClose(className);
joinPoint.proceed();
}
}
在app模塊新建Application,在onCreate中執行初始化:
public class App extends Application {
private static final String TAG = TraceAspect.class.getSimpleName();
@Override
public void onCreate() {
super.onCreate();
TrackPoint.init(new TrackCallBack() {
@Override
public void onClick(String pageName, String viewIdName) {
Log.d(TAG, "onClick:" + pageName + "-" + viewIdName);
//執行相應的業務
}
@Override
public void onPageOpen(String pageName) {
Log.d(TAG, "onPageOpen:" + pageName);
//執行相應的業務
}
@Override
public void onPageClose(String pageName) {
Log.d(TAG, "onPageClose:" + pageName);
//執行相應的業務
}
});
}
}
新增的Application需要在AndroidManifest中引用才會生效。
運行App後,點擊打開另一個Activity,然後依次退出Activity,輸出日誌如下:
2020-01-13 16:50:17.373 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageOpen:com.wangyz.aspectjdemo.MainActivity
2020-01-13 16:50:19.243 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onClick:com.wangyz.aspectjdemo.MainActivity-btn_open
2020-01-13 16:50:19.298 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageOpen:com.wangyz.aspectjdemo.SecondActivity
2020-01-13 16:50:21.392 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageClose:com.wangyz.aspectjdemo.SecondActivity
2020-01-13 16:50:22.320 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageClose:com.wangyz.aspectjdemo.MainActivity
為方法添加開始、結束的日誌
在library中增加註解AddLog
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AddLog {
}
在TraceAspect增加以下代碼
@Pointcut("execution(@com.wangyz.library.AddLog * *(..))")
public void addLogPointcut() {
}
@Around("addLogPointcut()")
public void addLog(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
AddLog addLog = signature.getMethod().getAnnotation(AddLog.class);
if (addLog != null) {
Object target = joinPoint.getTarget();
String className = "";
if (target != null) {
className = target.getClass().getName();
}
Log.d(TAG, "start execute:" + className + "-" + signature.getMethod().getName());
joinPoint.proceed();
Log.d(TAG, "end execute:" + className + "-" + signature.getMethod().getName());
} else {
joinPoint.proceed();
}
}
在MainActivity的onCreate上增加AddLog註解
@AddLog
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//...
}
運行App後,輸入日誌如下:
2020-01-13 16:50:17.373 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: start execute:com.wangyz.aspectjdemo.MainActivity-onCreate
2020-01-13 16:50:17.392 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: end execute:com.wangyz.aspectjdemo.MainActivity-onCreate
統計方法運行時間
在library中增加註解ExecTime
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExecTime {
}
在TraceAspect增加以下代碼
@Pointcut("execution(@com.wangyz.library.ExecTime * *(..))")
public void execTimePointcut() {
}
@Around("execTimePointcut()")
public void execTime(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
ExecTime execTime = signature.getMethod().getAnnotation(ExecTime.class);
if (execTime != null) {
long start = System.currentTimeMillis();
joinPoint.proceed();
long end = System.currentTimeMillis();
Object target = joinPoint.getTarget();
String className = "";
if (target != null) {
className = target.getClass().getName();
}
Log.d(TAG,
"execute time:" + className + "-" + signature.getMethod().getName() + " : " + (end - start) + "ms");
} else {
joinPoint.proceed();
}
}
在onClick方法上增加ExecTime註解
@ExecTime
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_open:
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
break;
default:
break;
}
}
運行App後,輸出日誌如下:
2020-01-13 16:50:19.272 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: execute time:com.wangyz.aspectjdemo.MainActivity-onClick : 28ms
源碼地址:https://github.com/milovetingting/Samples/tree/master/AspectJDemo