• Android埋点方案的简单实现-AOP之AspectJ


    个人博客

    http://www.milovetingting.cn

    Android埋点方案的简单实现-AOP之AspectJ

    AOP的定义

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

    以上关于AOP的定义引用自百度百科。

    AOP的运用场景

    日志记录、性能统计、权限控制、埋点等

    AOP的具体实现方案有很多,这里选用AspectJ来简单实现

    1. 监听View的点击、页面打开、关闭
    2. 为方法添加开始、结束的日志
    3. 统计方法运行时间

    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 {
        //...
        implementation project(':library')
    }
    

    监听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

  • 相关阅读:
    CF # 296 C Glass Carving (并查集 或者 multiset)
    linux 基本命令
    为什么是丰田——丰田的七个习惯之习惯一
    关注C-RAN 的五大理由
    一个效果非常华丽的仿桌面APP,却胜似Launcher
    IOS6.0自带下拉刷新控件UIRefreshControl
    POJ 2421--Constructing Roads【水题 && 最小生成树 && kruskal】
    ORACLE-017:SQL优化-is not null和nvl
    数据结构——栈
    数据结构——静态链表
  • 原文地址:https://www.cnblogs.com/milovetingting/p/12188399.html
Copyright © 2020-2023  润新知