美文网首页Android收藏集安卓Andorid的好东西
AOP埋点从入门到放弃(一)(2018年08月13日修改)

AOP埋点从入门到放弃(一)(2018年08月13日修改)

作者: 笔墨Android | 来源:发表于2018-08-15 16:26 被阅读128次

    今天老大跑过来说项目埋点了解一下!丢下了这句话之后,就没有之后了!剩下我一个人在风中凌乱!!!

    image

    其实这个需求老大在很久之前就说要开发了,后来就搁置了!但是今天看老大的态度,应该排到日程了!所以没办法只有硬着头皮磕了!免得过一阵子加班到很晚,所以趁着时间宽松,先能把踩的坑踩踩!!!分享给大家,也让大家能避免一些不必要的时间浪费。更好的过个周末,陪陪女盆友!!!


    特别声明:

    感谢JavaNoober提出的问题!

    问题是这样的?如果release的话,AspectJ失效怎么办?

    当时真的给我问懵逼了,这种查,这种百度,都解决不了!最后还是请教了大神才解决的!!!

    首先自己真的不了解配置这段代码的含义,所以产生了相应的问题,特别感谢您的指出。

        if (!variant.buildType.isDebuggable()) {
            log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
            return
        }
    

    这段代码的含义是在Debug的时候才执行的,如果不是Debug会直接返回的,所以呢?在你打release的时候,当然失效了。都return了!!!只要把这段代码去掉就可以了。


    本系列文章知识点:

    • 项目中埋点的需求分析
    • AOP思想的应用
    • AspectJ怎么集成到项目中(难点1)
    • AspectJ中的一些知识点说明(难点2)
    • AOP在项目中的应用等...

    出于可读性考虑,我准备把这个系列分成几部分去写,因为这样才能充分利用你的碎片时间,能让你在碎片化中学习一个知识点。

    第一篇文章主要讲解关于AOP中埋点的概念和相应的集成;
    第二篇文章主要讲解关于AspectJ中用到的一些知识点;
    第三篇文章主要讲解关于AspectJ在项目的其他一些应用。

    1.项目中埋点的需求分析

    1.1 首先先给菜鸟们科普一下什么叫做埋点

    所谓 埋点 ,百度百科是这么说的!其实说简单点,就是我在APP中都做了什么事情,让你们运营的知道,其实想想挺可怕的,这我要是出去浪,媳妇就知道了!!!明白了吧,你的一切行为都在掌控之中,用来生成人物画像什么的。。。一堆乱七八糟的!那么我们程序员要做什么呢?像什么统计时长了,点击了什么按钮了,常去什么页面了等...好吧!剩下的就看你们运营需要什么了,就科普到这里吧!

    1.2 常见的埋点方案

    我整理了相应内容,我发现其实埋点可以分为:

    • 服务器层面的:主要是通过APP端的请求进行分析
    • APP层面的:通过埋点进行相应的分析

    作为一个移动端的猿,理所应当的从APP层面去分析相应的实现,现在在APP端的实现基本上分为以下几种

    • 代码埋点:在需要的地方添加相应的代码,可谓是那里需要写哪里!!!但是缺点同时体现出来了,那就是代码量会成吨的输出,如果有一天你们项目经理跑过来改了某一个需求,代码更是成吨的增长,那个时候你会像"平安的程序员一样"奋起反抗的!!!
    • 自动化埋点:通过一些特殊手段(相应的切面编程AOP思想,这个也是本文要说的重点!!!),对相应的方法进行统计!
    • 第三方实现 现在很多第三方都有,百度、友盟等...只要按照说明文档就可以了!

    其实从程序员角度分析的话,无非就是代码写得多少的事情吗?往往许多内容都这能用这个东西衡量的,所以没有实现不了的,大不了我就多写点代码呗!但是为了让你成为一名有逼格的程序猿,总是要学点什么的!!!

    2. AOP思想的应用

    百度百科是这么形容AOP的!面向切面编程。也就是说在某个切面,你可以做一些相应的操作!这么和你比喻吧,当你触发一个点击事件的时候,点击的一瞬间算是一个切面,你可以在这个切面的前后加上一些相应的内容,也就是相应的切面编程了!

    能解决什么问题呢? 往往很多人都会这么问?有这样一个需求,一些APP只有在登陆的情况下才能做一些事情,往往有很多按钮都需要判断登陆的情况,如果你每一个按钮都写一个判断方法,那代码就很多了,如果产品跑过来说在添加一个VIP的功能你怎么办?所有的地方都要改?我擦,毁灭性的啊!这个时候就可以使用AOP这种编程思想了!再点击之前做一些相应的处理,那么即便是你在改的话,也只需要改一个地方!

    上面说了那么多都是废话,只是了解一下就可以了!我看Android中使用AOP基本上都是使用注解和一个叫AspectJ这么个东西,都说是非侵入式埋点,这个非侵入式是一个很好的东西,也就是不用更改之前代码的逻辑就可以实现相应的需求,所以我觉得埋点使用这个东西就非常好了!

    3. AspectJ怎么集成到项目中(难点1)

    关于AspectJ这个东西的集成,要用到一些gradle中的知识,其实对这里的知识我也不是很了解,也不再我们今天要讲的内容中,所以这里直接跳过了,感兴趣的同学可以自行百度,这个插一句(学习要有目的性,如果你要学某一个东西的话,其它的东西真的可以先放一放!!!)我就讲讲怎么集成就好了!!!

    3.1 添加相应的依赖

    首先说明一个事情,因为代码是非侵入性的,所以建议你把AspectJ集成在一个专门的Module中,这样在不改变原有的内容就能实现相应的方案。why?因为我就是这么做的。。。

    3.1.1 首先在 项目 的build.gradle中添加相应的依赖

        classpath 'org.aspectj:aspectjtools:1.8.9'
        //虽然都说句要加,但是我没加程序还是正常运行的!
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    
    位置图

    整段代码是这样滴!

    // Top-level build file where you can add configuration options common to all sub-projects/modules.
    
    buildscript {
        
        repositories {
            google()
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:3.1.4'
            classpath 'org.aspectj:aspectjtools:1.8.9'
            //我发现这个东西不加也是可以正常运行的
    //        classpath 'org.aspectj:aspectjweaver:1.8.9'
        }
    }
    
    allprojects {
        repositories {
            google()
            jcenter()
        }
    }
    
    task clean(type: Delete) {
        delete rootProject.buildDir
    }
    

    3.1.2 其次在项目中添加依赖和一些必要的配置

    在项目的build.gradle中添加相应的依赖implementation 'org.aspectj:aspectjrt:1.8.9'

    然后在 根路径 添加相应的配置

    import org.aspectj.bridge.IMessage
    import org.aspectj.bridge.MessageHandler
    import org.aspectj.tools.ajc.Main
    final def log = project.logger
    
    android.applicationVariants.all{ variant ->
        if (!variant.buildType.isDebuggable()) {
            log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
            return
        }
    
        JavaCompile javaCompile = variant.javaCompiler
        javaCompile.doLast {
            String[] args = ["-showWeaveInfo",
                             "-1.8",
                             "-inpath", javaCompile.destinationDir.toString(),
                             "-aspectpath", javaCompile.classpath.asPath,
                             "-d", javaCompile.destinationDir.toString(),
                             "-classpath", javaCompile.classpath.asPath,
                             "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
            log.debug "ajc args: " + Arrays.toString(args)
    
            MessageHandler handler = new MessageHandler(true)
            new Main().run(args, handler)
            for (IMessage message : handler.getMessages(null, true)) {
                switch (message.getKind()) {
                    case IMessage.ABORT:
                    case IMessage.ERROR:
                    case IMessage.FAIL:
                        log.error message.message, message.thrown
                        break
                    case IMessage.WARNING:
                        log.warn message.message, message.thrown
                        break
                    case IMessage.INFO:
                        log.info message.message, message.thrown
                        break
                    case IMessage.DEBUG:
                        log.debug message.message, message.thrown
                        break
                }
            }
        }
    }
    

    别问我为什么?我真的不理解这段代码,反正我知道这段代码是必须的。

    代码位置

    整段代码是这样滴!

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 28
        defaultConfig {
            applicationId "com.jinlong.aspectjdemo"
            minSdkVersion 14
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        implementation fileTree(include: ['*.jar'], dir: 'libs')
        implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
        implementation 'com.android.support.constraint:constraint-layout:1.1.2'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'com.android.support.test:runner:1.0.2'
        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
        implementation project(':aspectmodule')
        implementation 'org.aspectj:aspectjrt:1.8.9'
    }
    
    import org.aspectj.bridge.IMessage
    import org.aspectj.bridge.MessageHandler
    import org.aspectj.tools.ajc.Main
    final def log = project.logger
    
    android.applicationVariants.all{ variant ->
        if (!variant.buildType.isDebuggable()) {
            log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
            return
        }
    
        JavaCompile javaCompile = variant.javaCompiler
        javaCompile.doLast {
            String[] args = ["-showWeaveInfo",
                             "-1.8",
                             "-inpath", javaCompile.destinationDir.toString(),
                             "-aspectpath", javaCompile.classpath.asPath,
                             "-d", javaCompile.destinationDir.toString(),
                             "-classpath", javaCompile.classpath.asPath,
                             "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
            log.debug "ajc args: " + Arrays.toString(args)
    
            MessageHandler handler = new MessageHandler(true)
            new Main().run(args, handler)
            for (IMessage message : handler.getMessages(null, true)) {
                switch (message.getKind()) {
                    case IMessage.ABORT:
                    case IMessage.ERROR:
                    case IMessage.FAIL:
                        log.error message.message, message.thrown
                        break
                    case IMessage.WARNING:
                        log.warn message.message, message.thrown
                        break
                    case IMessage.INFO:
                        log.info message.message, message.thrown
                        break
                    case IMessage.DEBUG:
                        log.debug message.message, message.thrown
                        break
                }
            }
        }
    }
    

    在这里说明以下,如果你要是在Module中使用,那么在app的build.gradle中也要进行相应的配置!切记!!! 重要的事情说 "三遍"!!!这里直接贴一下相应Module中的配置!

    apply plugin: 'com.android.library'
    
    android {
        compileSdkVersion 28
    
        defaultConfig {
            minSdkVersion 14
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
    
        implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'com.android.support.test:runner:1.0.2'
        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    
        implementation 'org.aspectj:aspectjrt:1.8.9'
    }
    
    import org.aspectj.bridge.IMessage
    import org.aspectj.bridge.MessageHandler
    import org.aspectj.tools.ajc.Main
    final def log = project.logger
    
    android.libraryVariants.all{ variant ->
        if (!variant.buildType.isDebuggable()) {
            log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
            return
        }
    
        JavaCompile javaCompile = variant.javaCompiler
        javaCompile.doLast {
            String[] args = ["-showWeaveInfo",
                             "-1.8",
                             "-inpath", javaCompile.destinationDir.toString(),
                             "-aspectpath", javaCompile.classpath.asPath,
                             "-d", javaCompile.destinationDir.toString(),
                             "-classpath", javaCompile.classpath.asPath,
                             "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
            log.debug "ajc args: " + Arrays.toString(args)
    
            MessageHandler handler = new MessageHandler(true)
            new Main().run(args, handler)
            for (IMessage message : handler.getMessages(null, true)) {
                switch (message.getKind()) {
                    case IMessage.ABORT:
                    case IMessage.ERROR:
                    case IMessage.FAIL:
                        log.error message.message, message.thrown
                        break
                    case IMessage.WARNING:
                        log.warn message.message, message.thrown
                        break
                    case IMessage.INFO:
                        log.info message.message, message.thrown
                        break
                    case IMessage.DEBUG:
                        log.debug message.message, message.thrown
                        break
                }
            }
        }
    }
    
    注意点

    注意这里主项目和类库中是不一样的!!!以上可以保证你编译通过了,但是这才是开始的配置!!!

    3.1.3 配置相应的类

    这里先说以下相应的配置,具体为什么先不去说!!!下篇文章我会尽我所能给你讲解清楚的!!!相信我

    image
    @Aspect
    public class TraceAspect {
    
        private static final String TAG = "hjl";
    
        @Before("execution(* android.app.Activity.on*(..))")
        public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
            String key = joinPoint.getSignature().toString();
            Log.e(TAG, "onActivityMethodBefore: 切面的点执行了!" + key);
        }
    }
    

    首先说一下这段注意事项:

    • 顶部的注解@Aspect是不能少的,如果没有它一切都是扯淡!!!
    • @Before("execution(* android.app.Activity.on*(..))") 这段代码才是核心,先简单说一下,这段代码主要表述的内容是,检测所有activity中以on开头的方法,比如onCreate()然后前面的@Before说明的是在这个方法执行前执行里面的!这样你就可以运行程序,直接看LOG就可以了,

    简单说明一下原理,通过上面这个类,主要是在方法中的最开始添加一个相应的方法,也就是把你写的这段代码以一个方法的形式添加到某个位置!这样就实现了通过切面进行相应的处理方案了!!!

    想看源码吗?想看链接吗?点这里

    相关文章

      网友评论

      • JarryLeo:看了很多AOP方面的博客,真的是抄来超去都一样,那个判断debug代码全都一样,这段代码出处应该是hugo框架,这个框架是为debug测试用的,所以加了这个判断! 然后好多人被坑了,从而放弃了aop;我说那些人不看代码的吗.这几个单词应该认识啊:pensive:
        笔墨Android:@JarryLeo 也是别人告诉我的……:scream:共同进步,这也是分享的好处嘛

      本文标题:AOP埋点从入门到放弃(一)(2018年08月13日修改)

      本文链接:https://www.haomeiwen.com/subject/rupwbftx.html