个人博客
http://www.milovetingting.cn
Android埋点方案的简单实现-AOP之AspectJ
AOP的定义
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
以上关于AOP的定义引用自百度百科。
AOP的运用场景
日志记录、性能统计、权限控制、埋点等
AOP的具体实现方案有很多,这里选用AspectJ来简单实现
- 监听View的点击、页面打开、关闭
- 为方法添加开始、结束的日志
- 统计方法运行时间
AspectJ的使用
AspectJ的引入
这里引用AspectJX,AspectJX是基于AspectJ的一个AOP框架
新建Android工程,在项目根目录下的build.gradle文件中添加依赖
1 2 3 4 5
| dependencies { classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8' }
|
新建Module,类型选择Android Library,在新建的library的build.gradle文件中,添加相应的依赖
1
| apply plugin: 'android-aspectjx'
|
在app的build.gradle文件中增加对刚才新建的library的引用及AspectJ的依赖
1 2 3 4 5 6
| apply plugin: 'android-aspectjx'
dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' }
|
监听View的点击、页面打开、关闭
在library中新建回调接口TrackCallBack
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public interface TrackCallBack {
void onClick(String pageName, String viewIdName);
void onPageOpen(String pageName);
void onPageClose(String pageName);
}
|
在library中新建切入点TrackPoint
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class TrackPoint {
private static TrackCallBack mTrackCallBack;
private TrackPoint() {
}
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| @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中执行初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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,输出日志如下:
1 2 3 4 5
| 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
1 2 3 4
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AddLog { }
|
在TraceAspect增加以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @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注解
1 2 3 4 5 6 7
| @AddLog @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }
|
运行App后,输入日志如下:
1 2
| 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
1 2 3 4
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ExecTime { }
|
在TraceAspect增加以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @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注解
1 2 3 4 5 6 7 8 9 10 11 12
| @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后,输出日志如下:
1
| 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