今天老大跑過來說專案埋點了解一下!丟下了這句話之後,就沒有之後了!剩下我一個人在風中凌亂!!!
其實這個需求老大在很久之前就說要開發了,後來就擱置了!但是今天看老大的態度,應該排到日程了!所以沒辦法只有硬著頭皮磕了!免得過一陣子加班到很晚,所以趁著時間寬鬆,先能把踩的坑踩踩!!!分享給大家,也讓大家能避免一些不必要的時間浪費。更好的過個週末,陪陪女盆友!!!
特別宣告:
感謝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 配置相應的類
這裡先說以下相應的配置,具體為什麼先不去說!!!下篇文章我會盡我所能給你講解清楚的!!!相信我
@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就可以了,
簡單說明一下原理,通過上面這個類,主要是在方法中的最開始新增一個相應的方法,也就是把你寫的這段程式碼以一個方法的形式新增到某個位置!這樣就實現了通過切面進行相應的處理方案了!!!
想看原始碼嗎?想看連結嗎?點這裡