当前位置: 首页 > 工具软件 > DroidAssist > 使用案例 >

基于滴滴DroidAssist插件实现无痕埋点方案

方英耀
2023-12-01

无痕埋点概念

无痕埋点也叫无埋点、全埋点,它是一种数据采集的重要方法。由于业务更新迭代快、代码量大,手动埋点错误率高并且还费时费力,所以手动埋点不是明智的选择。这时候就需要无痕埋点,它不需要开发人员手动的写数据采集的代码,而是通过动态插入代码的方式,大大地减少了程序员的工作量。

无痕埋点方案

  • 方式一:全局Hook App中的View
  • 方式二:AOP编程(Aspect-Oriented Programming),操作字节码动态插入代码,常用框架Aspectj、ASM、javassist,三个框架的区别请参考这篇文章

优缺点

方式一:全局Hook App中View的方式原理是反射app中所有的View通过动态代理替换OnClickListener,效率低下,所以不采用这种方式。

方式二:AOP方式是在java->class、class->dex的过程中插入代码,由于是在编译的过程中执行只是增加了编译的时间,并不会App的效率。

DroidAssist

我们项目采用滴滴开源框架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") //插件配置文件(必选配置,支持多配置文件)
}

埋点配置表

ClassAgentNameSource
ViewonClickvoid android.view.View$OnClickListener.onClick(android.view.View)
DialogInterfaceonClickvoid android.content.DialogInterface$OnClickListener.onClick(android.content.DialogInterface,int)
RadioGrouponCheckedChangedvoid android.widget.RadioGroup$OnCheckedChangeListener.onCheckedChanged(android.widget.RadioGroup,int)
SeekBaronStopTrackingTouchvoid android.widget.SeekBar$OnSeekBarChangeListener.onStopTrackingTouch(android.widget.SeekBar)
RecyclerView-BaseQuickAdapteronItemClickvoid com.chad.library.adapter.base.BaseQuickAdapter$OnItemClickListener.onItemClick(com.chad.library.adapter.base.BaseQuickAdapter,android.view.View,int)
CompoundButtononCheckedChangedvoid 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>
    
 类似资料: