今天老大跑过来说项目埋点了解一下!丢下了这句话之后,就没有之后了!剩下我一个人在风中凌乱!!!
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就可以了,
简单说明一下原理,通过上面这个类,主要是在方法中的最开始添加一个相应的方法,也就是把你写的这段代码以一个方法的形式添加到某个位置!这样就实现了通过切面进行相应的处理方案了!!!
想看源码吗?想看链接吗?点这里
网友评论