无痕埋点也叫无埋点、全埋点,它是一种数据采集的重要方法。由于业务更新迭代快、代码量大,手动埋点错误率高并且还费时费力,所以手动埋点不是明智的选择。这时候就需要无痕埋点,它不需要开发人员手动的写数据采集的代码,而是通过动态插入代码的方式,大大地减少了程序员的工作量。
方式一:全局Hook App中View的方式原理是反射app中所有的View通过动态代理替换OnClickListener,效率低下,所以不采用这种方式。
方式二:AOP方式是在java->class、class->dex的过程中插入代码,由于是在编译的过程中执行只是增加了编译的时间,并不会App的效率。
我们项目采用滴滴开源框架DroidAssist,基于javassist,提供了一种简单易用、无侵入、配置化、轻量级的 Java 字节码操作方式,你不需要知道字节码相关的知识,只需要在 XML 配置中添加简单的 Java 代码即可实现编译期对 Class 文件的动态修改,项目地址
在 root project 的 build.gradle
里添加:
dependencies {
classpath "com.didichuxing.tools:droidassist:1.0.10"
}
在需要处理的 model project 的 build.gradle 里添加:
apply plugin: 'com.didichuxing.tools.droidassist'
droidAssistOptions {
config file("droidassist.xml") //插件配置文件(必选配置,支持多配置文件)
}
埋点配置表
Class | AgentName | Source |
---|---|---|
View | onClick | void android.view.View$OnClickListener.onClick(android.view.View) |
DialogInterface | onClick | void android.content.DialogInterface$OnClickListener.onClick(android.content.DialogInterface,int) |
RadioGroup | onCheckedChanged | void android.widget.RadioGroup$OnCheckedChangeListener.onCheckedChanged(android.widget.RadioGroup,int) |
SeekBar | onStopTrackingTouch | void android.widget.SeekBar$OnSeekBarChangeListener.onStopTrackingTouch(android.widget.SeekBar) |
RecyclerView-BaseQuickAdapter | onItemClick | void com.chad.library.adapter.base.BaseQuickAdapter$OnItemClickListener.onItemClick(com.chad.library.adapter.base.BaseQuickAdapter,android.view.View,int) |
CompoundButton | onCheckedChanged | void android.widget.CompoundButton$OnCheckedChangeListener.onCheckedChanged(android.widget.CompoundButton,boolean) |
xml配置
<BeforeMethodExecution>
<Source>void android.view.View$OnClickListener.onClick(android.view.View)</Source>
<Target>
{com.sxbcar.demo.util.AssistUtil.injectClick($1);}
</Target>
</BeforeMethodExecution>
<BeforeMethodExecution>
<Source>void android.content.DialogInterface$OnClickListener.onClick(android.content.DialogInterface,int)</Source>
<Target>
{com.sxbcar.demo.util.AssistUtil.injectDialogClick($1);}
</Target>
</BeforeMethodExecution>
<BeforeMethodExecution>
<Source>void android.widget.RadioGroup$OnCheckedChangeListener.onCheckedChanged(android.widget.RadioGroup,int)</Source>
<Target>
{com.sxbcar.demo.util.AssistUtil.injectCheckedChanged($1,$2);}
</Target>
</BeforeMethodExecution>
<BeforeMethodExecution>
<Source>void android.widget.SeekBar$OnSeekBarChangeListener.onStopTrackingTouch(android.widget.SeekBar)</Source>
<Target>
{com.sxbcar.demo.util.AssistUtil.injectSeekClick($1);}
</Target>
</BeforeMethodExecution>
<BeforeMethodExecution>
<Source>void com.chad.library.adapter.base.BaseQuickAdapter$OnItemClickListener.onItemClick(com.chad.library.adapter.base.BaseQuickAdapter,android.view.View,int)</Source>
<Target>
{com.sxbcar.demo.util.AssistUtil.injectRecyclerViewClick($1,$2,$3);}
</Target>
</BeforeMethodExecution>
<BeforeMethodExecution>
<Source>void android.widget.CompoundButton$OnCheckedChangeListener.onCheckedChanged(android.widget.CompoundButton,boolean)</Source>
<Target>
{com.sxbcar.demo.util.AssistUtil.injectCheckedChanged($1,$2);}
</Target>
</BeforeMethodExecution>
Activity
在onResume方法中获取activity中的window,然后获取DecorView,获取当前系统时间给DecorView设置Tag
public static void injectOnResume(Activity context) {
if(context.getWindow().getDecorView().getTag() == null){
long startTime = System.currentTimeMillis();
context.getWindow().getDecorView().setTag(startTime);
Log.e("----->", "injectOnResume-"+startTime);
}
}
在onStop方法中获取Tag,计算出停留时间
public static void injectOnStop(Activity context) {
if (context.getWindow().getDecorView().getTag() != null) {
long startTime=(long)context.getWindow().getDecorView().getTag();
long endTime = System.currentTimeMillis();
long dTime=endTime-startTime;
String s = TimeUtilKtKt.intervalMMSS(startTime, endTime);
context.getWindow().getDecorView().setTag(null);
Log.e("----->", "injectOnStop-time-"+s);
}
}
xml配置
<BeforeMethodExecution>
<Source>void android.app.Activity.onResume()</Source>
<Target>
{com.sxbcar.demo.util.AssistUtil.injectOnResume(this);}
</Target>
</BeforeMethodExecution>
<BeforeMethodExecution>
<Source>void android.app.Activity.onStop()</Source>
<Target>
{com.sxbcar.demo.util.AssistUtil.injectOnStop(this);}
</Target>
</BeforeMethodExecution>
Fragment
在onResume方法中获取fragment中的view,获取当前系统时间给DecorView设置Tag
public static void injectFragmentOnResume(Fragment fragment) {
if (fragment.getView() != null && fragment.getView().getTag() == null) {
long startTime = System.currentTimeMillis();
fragment.getView().setTag(startTime);
Log.e("----->", "injectFragmentOnResume-" + startTime);
}
}
在onPause方法中获取view并获取Tag,计算出停留时间
public static void injectFragmentonPause(Fragment fragment) {
if (fragment.getView() != null && fragment.getView().getTag() != null) {
long startTime = (long) fragment.getView().getTag();
long endTime = System.currentTimeMillis();
long dTime = endTime - startTime;
String s = TimeUtilKtKt.intervalMMSS(startTime, endTime);
fragment.getView().setTag(null);
Log.e("----->", "injectFragmentOnStop-" + s);
}}
xml配置
<BeforeMethodExecution>
<Source>void androidx.fragment.app.Fragment.onResume()</Source>
<Target>
{com.sxbcar.demo.util.AssistUtil.injectFragmentOnResume(this);}
</Target>
</BeforeMethodExecution>
<BeforeMethodExecution>
<Source>void androidx.fragment.app.Fragment.onPause()</Source>
<Target>
{com.sxbcar.demo.util.AssistUtil.injectFragmentonPause(this);}
</Target>
</BeforeMethodExecution>