原創(chuàng)不易,轉(zhuǎn)載請(qǐng)著名出處,謝謝
一. 全埋點(diǎn)概述
事件類型 | 事件定義 |
---|---|
AppStart | 應(yīng)用程序啟動(dòng),包含冷啟動(dòng)/熱啟動(dòng) |
AppEnd | 應(yīng)用程序退出,包含正常退出,home按下,程序強(qiáng)殺/崩潰 |
AppViewScreen | 頁面瀏覽,包含切換Activity/Fragment |
AppClick | 控件點(diǎn)擊 |
1. Android View 類型
序號(hào) | 控件名 | 監(jiān)聽方法 |
---|---|---|
1 | Button,CheckedTextView,TextView,ImageButton,ImageView | View.OnClickListener |
2 | SeekBar | SeekBar.OnSeekBarChangeListener |
3 | TabHost | TabHost.OnTabChangeListener |
4 | RatingBar | RatingBar.OnRatingBarChangeListener |
5 | CheckBox,SwitchCompat,RadioButton,ToggleButton,RadioGroup | CompoundButton.OnCheckChangeListener |
6 | Spinner | AdapterView.OnItemSelectListener |
7 | MenuItem | 重寫 Activity的 onOptionItemSelect,onContextItemSelect |
8 | ListView,GridView | AdapterView.OnItemSelectChangeListener |
9 | ExpandableListView | ExpandableListView.OnChildClickListener,ExpandableListView.OnGroupClickListener |
10 | Dialog | DialogInterface.OnClickListener,DialogInterface.OnMultiChoiceClickListener |
2. View 綁定listener方式
序號(hào) | 監(jiān)聽方法 |
---|---|
1 | 代碼方式 - 直接 setOnClickListener 監(jiān)聽 |
2 | xml - 中 android:onClick 綁定方法,在方法中監(jiān)聽 |
3 | butterKnife - 注解方法 @OnClick(xxx) ,在方法中監(jiān)聽 |
4 | lambda 方式 - setOnClickListener(v -> xxx)【aspectj不支持】 |
5 | dataBinding - android:onclick ="xx:xxx" ,在指定的xxx方法中監(jiān)聽 |
二. AppViewScreen 全埋點(diǎn)方案
源碼:https://github.com/wangzhzh/AutoTrackAppViewScreen
1. Application.ActivityLifecycleCallbacks
- 通過此registerActivityLifecycleCallbacks里面監(jiān)聽到onActivityResume上報(bào)AppViewScreen數(shù)據(jù)。
- 上報(bào)數(shù)據(jù)有event,deviceId,properties,time。
- 上報(bào)數(shù)據(jù)properties包含有appName,model,os-version,app-version,maunfacturer,width,height,os,lib-version,lib,activity。
2. 權(quán)限問題 READ_CONTACTS
6.0之后執(zhí)行運(yùn)行時(shí)權(quán)限回調(diào)onRequestPermissionResult 之后會(huì)再次執(zhí)行onResume導(dǎo)致頁面重復(fù)上報(bào)。
- 解決措施
制作ignore忽略類,在onRequestPermissionResult 回調(diào)中加入addIgnoreActivity,在onStop中 移除 removeIgnoreActivity,不上報(bào)的類也可以添加進(jìn)去,在上報(bào)之前判斷未忽略才執(zhí)行上報(bào)。
3. 頁面名稱采集
采集按照如下優(yōu)先級(jí):
- activity.getTitle
- sdkInt>=11 ,直接獲取getToolbarTitle{activity.getActionBar.getTitle/appCompatAct.getActionBar.gettitle}
- activity.packageManager.activityInfo.loadLabel
三. AppStart,AppEnd 全埋點(diǎn)方案
源碼:https://github.com/wangzhzh/AutoTrackAppStartAppEnd
1. 原理
AppStart : Application.registerActivityLifecycleCallbacks方法onActivityStarted中,執(zhí)行上報(bào),并通過ContentProvider+SQLite存儲(chǔ)標(biāo)記(作用是解決跨進(jìn)程數(shù)據(jù)共享問題,通過ContentObserver監(jiān)聽新進(jìn)頁面,標(biāo)記變化,如果在30s內(nèi),就取消上個(gè)頁面退出倒計(jì)時(shí),如果超30s,就執(zhí)行AppEnd上報(bào))
AppEnd : sdk初始化的時(shí)候創(chuàng)建定時(shí)器,Application.registerActivityLifecycleCallbacks方法onActivityStop時(shí),開啟定時(shí)器。30s后無新頁面進(jìn)入,執(zhí)行上報(bào),程序奔潰,強(qiáng)殺退出,下次進(jìn)入頁面需要補(bǔ)上報(bào) AppEnd
2. 缺點(diǎn)
因?yàn)槌绦虮紳ⅲ瑥?qiáng)殺,后面需要補(bǔ)上報(bào) AppEnd,如果用戶后面不在使用程序,或卸載程序,會(huì)導(dǎo)致 AppEnd 丟失
四. AppClick 全埋點(diǎn)方案 - 1:代理 View.OnClickenerListener
源碼:https://github.com/wangzhzh/AutoTrackAppClick1
1. 原理
在Application.registerActivityLifecycleCallbacks方法onActivityResume中,通過activity.getwindow.getDecorView獲取到其rootView,然后遞歸遍歷所有子控件,并對(duì)所有子控件的點(diǎn)擊事件設(shè)置代理攔截wrapperOnClickListener,其中有無點(diǎn)擊事件,通過反射View里面的mOnClickListener屬性判斷。
注意:
- 根據(jù)層級(jí)關(guān)系,DecorView是最頂層,子控件包含MenuItem及R.layout.content容器,所以為了能夠監(jiān)聽到MenuItem,取最頂層DecorView,不要取R.layout.content作為rootView,(與此同時(shí),獲取text需要加MenuItem類型的判斷)
- 為解決頁面中動(dòng)態(tài)添加控件問題,所以引入ViewTreeObserver.OnGlobalLayoutListener,所以此時(shí)邏輯變更了,在registerActivityLifecycleCallbacks方法onActivityCreate中創(chuàng)建OnGlobalLayoutListener監(jiān)聽器及監(jiān)聽器中遍歷綁定所有控件,在onActivityResume添加監(jiān)聽,在onActivityStop中移除監(jiān)聽
2. 上傳字段
- element_type: view.getclass.getCanonicalName
- element_id: 獲取view的id
- element_content: 獲取view的text
- activity: 包名+類名(通過context獲取包名,如果是contextWrapper類型,需要遞歸獲取getBaseContext,直至找到activity返回包名)
3. 拓展
控件名 | content獲取 | 監(jiān)聽方法(反射+代理) |
---|---|---|
Button,CheckedTextView,TextView | getText | View.OnClickListener |
ImageButton,ImageView | getContentDescription | View.OnClickListener |
CheckBox,SwitchCompat,RadioButton,ToggleButton | getText | CompoundButton.OnCheckChangeListener |
RadioGroup | 獲取選中的控件,在getText | CompoundButton.OnCheckChangeListener |
RatingBar | getRating | RatingBar.OnRatingBarChangeListener |
SeekBar | getPrgress | SeekBar.OnSeekBarChangeListener |
TabHost | 遍歷子控件,拼接文本 | TabHost.OnTabChangeListener |
Spinner | 遍歷子控件,拼接文本 | AdapterView.OnItemSelectChangeListener |
MenuItem | getMenuText | 重寫 Activity的 onOptionItemSelect,onContextItemSelect |
ListView,GridView | getPosition | AdapterView.OnItemSelectChangeListener |
ExpandableListView | Group position: child position | ExpandableListView.OnChildClickListener,ExpandableListView.OnGroupClickListener |
Dialog | getText | 獲取到rootView之后,在遍歷所有子控件,show添加/dismiss移除OnGlobalListener監(jiān)聽,點(diǎn)擊代理 DialogInterface.OnClickListener,DialogInterface.OnMultiChoiceClickListener |
五. AppClick 全埋點(diǎn)方案 - 2:代理 Window.CallBack
源碼:https://github.com/wangzhzh/AutoTrackAppClick2
1. 原理
Application.registerActivityLifecycleCallbacks方法onActivityCreate中,通過activity.getWindow.getcallBack,然后設(shè)置代理wrapperWindowCallback,通過這個(gè)代理類的dispatchTouchEvent,確定點(diǎn)擊的位置,然后從控件列表集合中找到具體的控件,插入埋點(diǎn)代碼。
判斷控件是否是集合中的哪個(gè)控件,需要滿足的條件:
- view.visible==view.visible
- view.isClickable==true
- MotionEvent的x,y坐標(biāo)必須處于view內(nèi)部
2. 拓展
控件名 | 判斷規(guī)則(默認(rèn)滿足上面1,2,3條件) |
---|---|
RatingBar | 4.view是ratingBar類型 |
SeekBar | 4.view是SeekBar類型 |
Spinner | 采用代理方式處理,代理 dapterView.OnItemSelectChangeListener |
ListView,GridView | 采用代理方式處理,代理 ExpandableListView.OnChildClickListener,ExpandableListView.OnGroupClickListener |
六. AppClick 全埋點(diǎn)方案 - 3:代理 View.AccessibilityDelegate
源碼:https://github.com/wangzhzh/AutoTrackAppClick3
1. 原理
在Application.registerActivityLifecycleCallbacks方法onActivityResume中,通過activity.getwindow.getDecorView獲取到其rootView,然后遞歸遍歷所有子控件,并對(duì)所有子控件設(shè)置代理攔截mAccessibilityEvent,埋點(diǎn)代碼就在其回調(diào)方法中處理。
2. 拓展
ratingBar/SeekBar/Spinner/ListView,GradView/ExpandableListView 均與之前《第四章View.OnClickenerListener》反射+動(dòng)態(tài)代理方案一致
3. 缺點(diǎn)
- 使用反射,效率低,有版本兼容問題
- 需要開啟輔助功能,部分Android Rom機(jī)型上可能會(huì)失效
七. AppClick 全埋點(diǎn)方案 - 4:透明層
源碼:https://github.com/wangzhzh/AutoTrackAppClick4
1. 原理
在activity的最上層添加一個(gè)透明的View,然后重寫透明view的onTouchEvent,從里面取出xy位置,判斷控件集合的具體控件,然后使用wrapperOnClickListener代理其mOnclickListener對(duì)象,并在代理類中實(shí)現(xiàn)埋點(diǎn)上報(bào)。
透明層條件:
- width/height需是layout.MATCH_PARENT
- 設(shè)置透明層在最上層,view.setElevation(xxx,999f)
- decorView.addView(xxx)
判斷控件是否在控件集合中,與之前《第六章View.AccessibilityDelegate》的尋找方法一致
2. 拓展
與《第五章 Window.CallBack》方案一致
七. AppClick 全埋點(diǎn)方案 - 5:Aspectj
源碼:https://github.com/wangzhzh/AutoTrackAppClick5
1. Aspectj
AOP 面向切面編程,可實(shí)現(xiàn)的有日志埋點(diǎn),性能監(jiān)控,動(dòng)態(tài)權(quán)限控制,代碼調(diào)試
Aspectj 使用ajc編譯器,在編譯期把代碼插入目標(biāo)程序中
Aspectj簡(jiǎn)單使用:Aspectj簡(jiǎn)單使用
使用AspectJ的2種方式:
- 簡(jiǎn)單的配置Aspectj:https://github.com/wangzhzh/AutoTrackAspectJProject1
- 自定義Gradle Plugin:https://github.com/wangzhzh/AutoTrackAspectJProject2
2. 擴(kuò)展View屬性
通過給控件setTag(int,object)的方式支持拓展,后續(xù)從view中取出這個(gè)值使用,但是為了保證tag的key不重復(fù),需要在xml中定義資源id,使用時(shí)就使用它即可
3. 無法采集情況
無法采集的情況 | 解決思路 | aspectj代碼 |
---|---|---|
butterknife的onClick注解綁定的事件 | 新增對(duì)onClick有參數(shù)情況的切入點(diǎn),無參數(shù)暫不考慮 | @After("execution(@butterKnife.onclick **(android.view.View))") |
xml android:onclick屬性綁定的事件 | 新增一個(gè)注解,然后加在此xml指定的方法上 | @After("execution(@xxx **(android.view.View))") |
MenuItem的點(diǎn)擊事件 | 新增2個(gè)menuItem監(jiān)聽的2方法 | @After("execution(@android.app.Activity.onOptionItemSelected(android.view.MenuItem))") @After("execution(@android.app.Activity.onContextItemSelected(android.view.MenuItem))") |
設(shè)置onclickListener使用了lambda語法 | aspectj暫不支持lambda語法,所以無法解決 |
4. 拓展
控件名 | aspectj代碼 |
---|---|
AlertDialog | @After("execution(@android.content.dialogInterface.onClickListener.onClick(android.content.dialogInterface,int))") @After("execution(@android.content.dialogInterface.onMultiChoiceClickistener.onClick(android.content.dialogInterface,int,Boolean))") |
CheckBox,SwitchCompat,RadioButton,ToggleButton,RadioGroup | @After("execution(@android.widget.CompoundButton.OnCheckChangeListener.onCheckChanged(android.widget.CompoundButton,Boolean))") |
RatingBar | @After("execution(@android.widget.RatingBar.OnRatingBarChangeListener.onRatingChanged(android.widget.RatingBar,float,Boolean))") |
SeekBar | @After("execution(@android.widget.SeekBar.OnSeekBarChangeListener.onStopTrackingTouch(android.widget.RatingBar,float,Boolean))") |
Spinner | @After("execution(@android.widget.AdapterView.OnItemSelectListener.onItemSelected(android.widget.AdapterView,android.view.View,int,long))") |
TabHost | @After("execution(@android.widget.TabHost.OnTabChangeListener.onTabChanged(String))") |
ListView,GridView | @After("execution(@android.widget.AdapterView.OnItemSelectChangeListener.onItemClick(android.widget.AdapterView,android.view.View,int,long))") |
ExpandableListView | @After("execution(@android.widget.ExpandableListView.OnChildClickListener.onChildClick(android.widget.ExpandableListView,android.view.View,int,long))") @After("execution(@android.widget.ExpandableListView.OnGroupClickListener.onGroupClick(android.widget.ExpandableListView,android.view.View,int,long))") |
5. 缺點(diǎn)
- 無法織入第三方庫
- 無法兼容Lambda語法
- 有兼容性問題,D8、Gradle4.X
七. AppClick 全埋點(diǎn)方案 - 6:ASM
1. ASM
Android gradle 1.5.0之后,提供了transfrom API ,允許第三方插件形式,在安卓打包過程中操作.class文件,遍歷類,jar包等,在此過程中可再使用字節(jié)碼操作工具ASM去操作,去訪問具體的類,從類中讀取類名,方法,屬性等,然后通過字節(jié)碼指令去修改原有的類(例如:訪問到onClick方法,并在方法結(jié)束之前加一段埋點(diǎn)上報(bào)代碼),然后在將修改好的類,繼續(xù)執(zhí)行打包task,后續(xù)apk中就有了此上報(bào)邏輯。
涉及到的2個(gè)技術(shù)點(diǎn):
Gradle Transfrom
transfrom 簡(jiǎn)單api
實(shí)例:https://github.com/wangzhzh/AutoTrackTransformProjectASM
ASM 簡(jiǎn)單api
實(shí)例:https://github.com/wangzhzh/AutoTrackAppClick6
實(shí)質(zhì)就是在方法訪問器中,方法結(jié)束里面判斷接口如果實(shí)現(xiàn)了點(diǎn)擊事件接口,并且方法是onclick()方法,就插入一段埋點(diǎn)上報(bào)的代碼,達(dá)到自動(dòng)埋點(diǎn)目的
2. 無法采集情況
無法采集的情況 | 解決思路 | ASM代碼 |
---|---|---|
xml android:onclick屬性綁定的事件 | 新增一個(gè)注解,然后加在此xml指定的方法上,繼續(xù)visitorAnnotation中找到此注解,設(shè)置標(biāo)識(shí),并在此方法結(jié)束之后插入埋點(diǎn)代碼 | isFlag=true&&desc=='(Landroid/view/View;)V' |
4. 拓展
所有的操作都是在方法訪問器,結(jié)束方法中判斷是否達(dá)到條件,滿足則加入埋點(diǎn)字節(jié)碼
控件名 | ASM判斷代碼 |
---|---|
AlertDialog | mInterface.conteins('android/content/DialogInterface |
MenuItem | nameDesc=='onContextItemSelected(Landroid/view/MenuItem;Z)V' 或 nameDesc=='onOptionsItemSelected(Landroid/view/MenuItem;Z)V' |
CheckBox,SwitchCompat,RadioButton,ToggleButton,RadioGroup | mInterface.conteins('android/widget/CompoundButton$OnCheckChangeListener')&&nameDesc=='onCheckChanged(Landroid/content/CompoundButton;Z)V' |
RatingBar | mInterface.conteins('android/widget/RatingBar$OnRatingBarChangeListener')&&nameDesc=='onRatingChanged(Landroid/content/RatingBar;FZ)V' |
SeekBar | mInterface.conteins('android/widget/SeekBar$OnSeekBarChangeListener')&&nameDesc=='onStopTrackingTouch(Landroid/content/SeekBar;)V' |
Spinner | mInterface.conteins('android/widget/AdapterView$OnItemSelectListener')&&nameDesc=='onItemSelected(Landroid/content/AdapterView;Landroid/view/View;IJ)V |
TabHost | mInterface.conteins('android/widget/TabHost$OnTabChangeListener')&&nameDesc=='onTabChanged(Ljava/lang/String;)V |
ListView,GridView | mInterface.conteins('android/widget/AdapterView$OnItemClickListener')&&nameDesc=='onItemClick(Landroid/content/AdapterView;Landroid/view/View;IJ)V |
ExpandableListView | mInterface.conteins('android/widget/ExpandableListView |
七. AppClick 全埋點(diǎn)方案 - 7:Javassist
1. javassist
與ASM類似,為字節(jié)碼操作工具。那么處理流程也是通過transfrom遍歷文件找到指定類,然后通過 javassist處理指定文件,實(shí)現(xiàn)代碼注入。
- javassist
javassist api介紹
實(shí)例:https://github.com/wangzhzh/AutoTrackAppClick7
實(shí)質(zhì)上也是攔截到點(diǎn)擊事件接口,及 點(diǎn)擊方法,然后通過ctMethod的insertAfter插入埋點(diǎn)代碼
2. 拓展
所有的操作都是在獲取到所有接口數(shù)組,遍歷方法,斷是否達(dá)到條件,滿足則通過method.insertAfter加入埋點(diǎn)字節(jié)碼
控件名 | javassist判斷代碼(nameDesc=method.name+emthod.getSignature)) |
---|---|
xml android:onclick屬性綁定的事件 | 新增一個(gè)注解,然后加在此xml指定的方法上。annotation== xxx && 'currentMethod.getSignature=='(Landroid/view/View;)V'' |
AlertDialog | mInterface.conteins('android/content/DialogInterface |
MenuItem | nameDesc=='onContextItemSelected(Landroid/view/MenuItem;Z)V' 或 nameDesc=='onOptionsItemSelected(Landroid/view/MenuItem;Z)V' |
CheckBox,SwitchCompat,RadioButton,ToggleButton,RadioGroup | mInterface.conteins('android/widget/CompoundButton$OnCheckChangeListener')&&nameDesc=='onCheckChanged(Landroid/content/CompoundButton;Z)V' |
RatingBar | mInterface.conteins('android/widget/RatingBar$OnRatingBarChangeListener')&&nameDesc=='onRatingChanged(Landroid/content/RatingBar;FZ)V' |
SeekBar | mInterface.conteins('android/widget/SeekBar$OnSeekBarChangeListener')&&nameDesc=='onStopTrackingTouch(Landroid/content/SeekBar;)V' |
Spinner | mInterface.conteins('android/widget/AdapterView$OnItemSelectChangeListener')&&nameDesc=='onItemSelected(Landroid/content/AdapterView;Landroid/view/View;IJ)V |
TabHost | mInterface.conteins('android/widget/TabHost$OnTabChangeListener')&&nameDesc=='onTabChanged(Ljava/lang/String;)V |
ListView,GridView | mInterface.conteins('android/widget/AdapterView$OnItemClickListener')&&nameDesc=='onItemClick(Landroid/content/AdapterView;Landroid/view/View;IJ)V |
ExpandableListView | mInterface.conteins('android/widget/ExpandableListView |
八. AppClick 全埋點(diǎn)方案 - 8:AST
源碼:https://github.com/wangzhzh/AutoTrackAppClick8
1. APT
- APT
APT 簡(jiǎn)單api
實(shí)例:https://github.com/wangzhzh/AutoTrackAPTProject
實(shí)質(zhì)就是對(duì)頁面某個(gè)控件添加注解,然后此注解生成器會(huì)編譯時(shí)生成添加了注解的類的輔助埋點(diǎn)上報(bào)類,在registerActivityLifecycleCallbacks的創(chuàng)建方法會(huì)找到此注解類,然后執(zhí)行上報(bào)邏輯
2. AST
抽象語法樹,用樹的形式表示源代碼,源代碼每個(gè)元素映射到一個(gè)節(jié)點(diǎn)或子樹。
編譯器對(duì)代碼的處理流程是:JavaTxt->詞語法分析->生成AST->語義分析->編譯字節(jié)碼,通過操作AST,達(dá)到修改源代碼目的。
具體流程:
- 注解處理器的process方法
- element=roundEnvironment.getRootElements
- tree=trees.getTree(element)
- 自定義一個(gè)TreeTranslator,執(zhí)行tree.accept(this)
- 在TreeTranslator的visitMethodDef找到指定方法,通過AST框架插入埋點(diǎn)代碼
3. 無法采集情況
無法采集的情況 | 解決思路 | AST代碼 |
---|---|---|
butterknife的onClick注解綁定的事件 | AST遍歷注解時(shí)判斷@OnClick,且方法是onClick,無返回void,參數(shù)1個(gè) | jcMethodDecl.getName==onClick&&jcMethodDecl.getParameters==void&&jcMethodDecl.getParameters.size==1 |
xml android:onclick屬性綁定的事件 | 新增一個(gè)注解,然后加在此xml指定的方法上 | jcMethodDecl.getName==onClick&&jcMethodDecl.getParameters==void&&jcMethodDecl.getParameters.size==1 |
設(shè)置onclickListener使用了lambda語法 | AST暫不支持lambda語法,所以無法解決 |
4. 拓展
主要根據(jù)返回值,方法名,方法參數(shù)個(gè)數(shù)及類型判斷,故封裝一個(gè)公用類統(tǒng)一判斷
控件名 | AST代碼 |
---|---|
AlertDialog | 'onclick,void,Collections.singletonList(View),After' 'onclick,void,Arrays.asList(dialogInterface,int),After' 'onclick,void,Arrays.asList(dialogInterface,int,boolean),After' |
MenuItem | 'onOptionsItemSelected,boolean,Collections.singletonList(MenuItem),After' 'onContextItemSelected,boolean,Collections.singletonList(MenuItem),After' |
CheckBox,SwitchCompat,RadioButton,ToggleButton,RadioGroup | 'onCheckedChanged.void,Arrays.asList(CompoundButton,boolean),After' |
RatingBar | 'onRatingChanged,vpid,Arrays.asList(RatingBar,boolean),After' |
SeekBar | 'onStopTrackingTouch,void,Collections.singletonList(SeekBar),After' |
Spinner | 'onItemSelected,void,Arrays.asList(AdapterView<?>,View,int,long),After' |
TabHost | 'onTabChanged,void,Collections.singletonList(String),After |
ListView,GridView | 'onItemClick,void,Arrays.asList(AdapterView<?>,View,int,long),After' |
ExpandableListView | 'onGroupClick,boolean,Arrays.asList(ExpandableListView,View,int,long),Before' 'onChildClick,boolean,Arrays.asList(ExpandableListView,View,int,int,long),Before' |
5. 缺點(diǎn)
- com.sun.tools.javac.tree APi語法晦澀,理解難度大
- APT無法掃描其他Module
- 不支持lambda語法
- 有返回值的方法,很難把埋點(diǎn)代碼插入方法之后
最佳方案總結(jié)
由于本人曾參與公司埋點(diǎn)SDK的研發(fā),所以對(duì)其有一套自己的理解和感悟,總結(jié)了一種最佳的方案,其方案如下
1. 上報(bào)事件的方案選擇
點(diǎn)擊事件,上報(bào)方案選擇
使用asm的方案是最好,最簡(jiǎn)單的,不會(huì)影響運(yùn)行時(shí)的時(shí)間,直接執(zhí)行點(diǎn)擊攔截上報(bào)-
頁面進(jìn)入/離開事件,上報(bào)方案選擇
如果是activity的頁面進(jìn)入離開,直接通過Application.registerActivityLifecycleCallbacks可以直接監(jiān)聽上報(bào)。如果希望fragment/dialog/dialogFragment/popupwindow也可以上報(bào),可以制作他們的基類,并在基類的進(jìn)入離開,加入埋點(diǎn)上報(bào)代碼,制作transfrom 插件,通過asm方式去替換父類,注意:(此處不僅僅是通過類訪問器找到父類,簡(jiǎn)單的替換父類,還需要導(dǎo)入常量池修改庫替換常量池里面的父類,達(dá)到構(gòu)造方法也同步修改,否則修改失效)
冷熱啟動(dòng)事件,上報(bào)方案選擇
通過Application.registerActivityLifecycleCallbacks統(tǒng)計(jì)activity的有無的個(gè)數(shù),統(tǒng)計(jì)冷熱啟動(dòng)狀態(tài),執(zhí)行上報(bào)前臺(tái),后臺(tái)事件,上報(bào)方案選擇
可使用此書中的方案,開啟倒計(jì)時(shí)30s,或者直接根據(jù)registerActivityLifecycleCallbacks統(tǒng)計(jì)當(dāng)前activity的狀態(tài)判斷也可,根據(jù)業(yè)務(wù)而定曝光/業(yè)務(wù)/xxx上報(bào)
直接在上報(bào)sdk中提供上報(bào)方法即可
2. 處理流程
構(gòu)造上報(bào)對(duì)象:sdk需創(chuàng)建線程池,所有的上報(bào)都應(yīng)該在線程池中執(zhí)行
加入消息隊(duì)列:創(chuàng)建handler線程,使用此線程隊(duì)列保證消息的次序,待上報(bào)在線程池構(gòu)造成功具體的上報(bào)對(duì)象,統(tǒng)一封裝成消息,發(fā)送到消息隊(duì)列
存入數(shù)據(jù)庫:消息隊(duì)列取出消息,執(zhí)行插入數(shù)據(jù)庫操作,并加入判斷100條,執(zhí)行上報(bào),或者3分鐘上報(bào)數(shù)據(jù)庫的上報(bào)數(shù)據(jù)
執(zhí)行上報(bào):從數(shù)據(jù)庫取出消息,執(zhí)行okHttp的上報(bào),并且處理上報(bào)成功刪除數(shù)據(jù)庫數(shù)據(jù),及重試機(jī)制
3. transfrom 編譯優(yōu)化
- 開啟增量編譯,處理好增量編譯
- 創(chuàng)建線程池,在線程池中執(zhí)行遍歷文件,jar,提高同步編譯速度
- 設(shè)置debug不開啟編譯,release開啟編譯,類似的動(dòng)態(tài)配置開關(guān),解決不需要此編譯項(xiàng)不讓其拉低apk編譯速度