全自动采集最新行业文章(Android全埋点解决方案王灼洲著第1章全埋点著概述)

优采云 发布时间: 2021-12-25 05:20

  全自动采集最新行业文章(Android全埋点解决方案王灼洲著第1章全埋点著概述)

  点击查看第二章

  点击查看第三章

  Android全埋点解决方案

  

  王卓舟第一章

  所有埋点概览

  全埋点,又称无埋点、*敏*感*词*埋点、无痕埋点、自动埋点。全埋点是指无需Android应用开发工程师编写代码或只编写少量代码,即可提前自动采集所有用户行为数据,然后根据实际业务分析过滤分析所需的行为数据要求 。

  所有埋点采集的事件目前主要包括以下四种(事件名称前面的$符号表示该事件为预设事件,对应的事件为自定义事件)。

  □$AppStart 事件

  指应用启动,包括冷启动和热启动两种场景。热启动是指应用程序从后台恢复的情况。

  □$AppEnd 事件

  指应用退出,包括应用正常退出、按Home键进入后台、应用被强杀、应用崩溃等场景。

  □$AppViewScreen 事件

  指应用页面浏览。对于安卓应用,是指切换Activity或者Fragment。

  □$AppClick 事件

  指应用控件点击,即View被点击,比如点击Button、ListView等。

  采集的四个事件中,最重要和采集的难点是$AppClick事件。因此,整点的解决方案基本上是围绕如何采集 $AppClick 事件。

  对于$AppClick事件全埋点的整体解决方案,归根结底就是自动找到被点击控件的处理逻辑(以下统称原创处理逻辑),然后利用一定的技术原理来“拦截”原来的处理逻辑。, 或者在原处理逻辑执行前或者执行器后面“插入”相应的埋藏代码逻辑,从而达到自动埋藏的效果。

  至于如何自动“拦截”控件的原创处理逻辑,一般参考Android系统的事件处理机制来进行。关于Android系统的事件处理机制,限于篇幅,本书不再详述。

  至于如何自动“插入”嵌入的代码逻辑,基本上是指编译器对Java代码的整体处理流程,即:

  JavaCode --> .java --> .class --> .dex

  选择在不同的处理阶段“插入”嵌入点代码,使用的技术或原理不尽相同,因此对于全嵌入点有多种解决方案。

  面对这么多全嵌入式解决方案,我们应该如何选择?

  在选择全埋点方案时,需要从效率、兼容性、扩展性等方面综合考虑。

  □效率

  全埋点的基本原理,如上所说,其实就是利用某种技术拦截(或调用代理)某些方法(控件点击时的处理逻辑)或“插入”相关埋点代码。比如按钮Button,如果要为其设置点击处理逻辑,则需要设置android.view.View.OnClickListener并覆盖其onClick(android.view.View)方法。如果要实现$AppClick事件的全埋,可以“拦截”onClick(android.view.View)方法,或者在onClick(android.view.View)前后“插入”相应的埋藏逻辑方法代码。根据“什么时候去代理或插入代码”的条件来区分,

  □静态代理

  所谓静态代理,是指通过Gradle Plugin在应用编译过程中“插入”代码或修改代码(.class文件)。如AspectJ、ASM、Javassist、AST等程序都属于这种方式。后面我们会一一介绍这几种方案。

  有关这些方法的时序,请参见图 1-1。

  

  □动态代理

  所谓动态代理是指代码运行时的代理(Runtime)。比如我们比较常见的代理View.OnClickListener、Window.Callback、View.AccessibilityDelegate等程序都属于这种方式。后面我们会一一介绍这几种方案。

  不同的解决方案具有不同的处理能力和运行效率,同时对应用程序的入侵程度和对应用程序整体性能的影响也不同。一般来说,静态代理明显优于动态代理。这是因为静态代理的“动作”是在应用程序的编译阶段处理的,不会对应用程序的整体性能产生太大影响。“动作”发生在应用程序的运行阶段(即Runtime),因此会对应用程序的整体性能产生一定的影响。

  □兼容性

  随着Android生态系统的快速发展,无论是Android系统本身,还是Android应用开发相关的组件和技术都在快速发展、迭代迅速,这也给我们开发全埋解决方案带来了一定的困难。例如,不同的Android应用可以有不同的开发语言(Java、Kotlin)、不同的Java版本(Java7、Java8)、不同的开发IDE(eclipse、Android Studio)等等。方法(原生开发,H5、混合开发),使用不同的第三方开发框架(React Native,APICloud,Weex),不同的Gradle版本,以及Lambda,D8、Instant Run,

  □可扩展性

  随着业务的快速发展和数据分析需求的不断提高,对数据的全埋点使用提出了更高的要求采集。一方面,它需要全自动化的采集(采集的范围),同时它需要更精细的采集控制粒度(采集可以是定制)。例如,如何向控件添加自定义属性?如果不想采集怎么控制某个控件的点击事件?如果不想采集某类控件(ImageView)点击事件,如何处理?如果某个页面(Activity)上所有控件的点击事件都不要采集,如何处理等。

  任何一种全埋点技术方案都有利有弊,没有普遍的完美方案。我们只需要针对不同的应用场景选择最适合的数据解决方案。能够满足采集实际数据需求的方案就是最好的方案。

  1.1Android 视图类型

  在Android系统中,控件(View)的种类非常丰富。分类方法也多种多样。根据控件设置的不同*敏*感*词*器,我们大致可以将控件分为以下几类。

  □Button、CheckedTextView、TextView、ImageButton、ImageView等。

  为这些控件设置的侦听器是 android.view.View.OnClickListener。

  以按钮为例:

  按钮按钮 = findViewById(R.id.button);

  button.setOnClickListener(new View.OnClickListener() {

  @Override

public void onClick(View view) {

//do something

}

  });

  □搜索栏

  SeekBar设置的*敏*感*词*器为android.widget.SeekBar.OnSeekBarChangeListener,如:

  SeekBar seekBar = findViewById(R.id.seekBar);

  seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

  @Override

public void onProgressChanged(SeekBar seekBar, int i, boolean b) {

// do something

}

@Override

public void onStartTrackingTouch(SeekBar seekBar) {

// do something

}

@Override

public void onStopTrackingTouch(SeekBar seekBar) {

// do something

}

  });

  □TabHost

  TabHost设置的*敏*感*词*器为android.widget.TabHost.OnTabChangeListener,如:

  TabHost tabHost = findViewById(R.id.tabhost);

  tabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {

  @Override

public void onTabChanged(String tabName) {

//do something

}

  });

  □评级栏

  RatingBar设置的listerner为android.widget.RatingBar.OnRatingBarChangeListener,如:

  RatingBar ratingBar = findViewById(R.id.ratingBar);

  ratingBar.setOnRatingBarChangeListener(newRatingBar.OnRatingBarChangeListener(){

  @Override

public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {

//do something

}

  });

  □CheckBox、SwitchCompat、RadioButton、ToggleButton、RadioGroup等。

  这些View属于同一种类型,都是有“状态”的按钮,它们设置的*敏*感*词*器是CompoundButton.OnCheckedChangeListener。

  以 CheckBox 为例:

  CheckBox checkBox = findViewById(R.id.checkbox);

  checkBox.setOnCheckedChangeListener(newCompoundButton.OnCheckedChangeListener(){

  @Override

public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {

//do something

}

  });

  □纺纱机

  Spinner设置的*敏*感*词*器为android.widget.AdapterView.OnItemSelectedListener,如:

  Spinner spinner = findViewById(R.id.spinner);

  spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

  @Override

public void onItemSelected(AdapterView parent, View view, int position, long id) {

//do something

}

@Override

public void onNothingSelected(AdapterView parent) {

}

  });

  □菜单项

  *敏*感*词*器主要是通过覆盖Activity的相关方法(onOptionsItemSelected、onContextItemSelected)来设置的,比如:

  //选项菜单

  @覆盖

  公共布尔 onOptionsItemSelected(android.view.MenuItem) {

  //do something

  }

  //上下文菜单

  @覆盖

  公共布尔 onContextItemSelected(android.view.MenuItem) {

  //do something

  }

  □ListView、GridView

  ListView和GridView都是AdapterView的子类,显示的内容是一个“集合”。他们设置的*敏*感*词*器是android.widget.AdapterView.OnItemClickListener,比如:

  ListView listView = findViewById(R.id.listView);

  listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){

  @Override

public void onItemClick(AdapterView parent, View view, int position, long id) {

//do something

}

  });

  □可扩展列表视图

  ExpandableListView 也是 AdapterView 的子类和 ListView 的子类。它的点击分为两种情况,ChildClick和GroupClick,所以它设置的*敏*感*词*器也分为两种情况,即:android.widget.ExpandableListView.OnChildClickListener和android.widget.ExpandableList-View.OnGroupClickListener,如:

  ExpandableListView listview = findViewById(R.id.expandablelistview);

  //子点击

  listview.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {

  @Override

public boolean onChildClick(ExpandableListView expandableListView,

View view, int groupPosition, int childPosition, long id) {

//do something

return true;

}

  });

  //组点击

  listview.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

  @Override

public boolean onGroupClick(ExpandableListView expandableListView,

View view, int childPosition, long id) {

//do something

return true;

}

  });

  □对话

  Dialog设置的*敏*感*词*器分为两种情况。对于普通的普通Dialog,设置的*敏*感*词*器是android。content.DialogInterface.OnClickListener,如:

  AlertDialog.Builder builder = new AlertDialog.Builder(context);

  builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {

  @Override

public void onClick(DialogInterface dialog, int which) {

//do something

}

  });

  还有一个显示列表的对话框。它设置的*敏*感*词*器是android.content.DialogInterface.OnMultiChoiceClickListener,比如:

  AlertDialog.Builder builder = new AlertDialog.Builder(context);

  DialogInterface.OnMultiChoiceClickListener mutiListener =

   new DialogInterface.OnMultiChoiceClickListener() {

@Override

public void onClick(DialogInterface dialogInterface, int which, boolean isChecked) {

//do something

}

  };

  1.2View绑定*敏*感*词*方法

  随着Android相关技术的不断更新和迭代,将*敏*感*词*器绑定到View的方式有很多种。下面以Button为例介绍一下日常开发中几种常见的绑定*敏*感*词*器的方式。

  □通过代码设置*敏*感*词*器

  按钮按钮 = findViewById(R.id.button);

  button.setOnClickListener(new View.OnClickListener() {

  @Override

public void onClick(View view) {

//do something

}

  });

  这种方法是目前开发中最常用的方法,也是我们全埋点解决方案需要重点关注和支持的方法。

  □通过android:onClick属性绑定*敏*感*词*器

  首先在布局文件中声明Button的android:onClick属性,如:

  android:id="@+id/xmlOnClick"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:onClick="xmlOnClick"

android:text="android:onClick 绑定OnClickListener"/>

  我们将android:onClick的属性值设置为“xmlOnClick”,此时“xmlOnClick”代表点击处理逻辑对应的方法名。然后在对应的Activity文件中声明android:onClick属性指定的方法xmlOnClick:

  公共无效xmlOnClick(查看视图){

  //do something

  }

  注意:此方法必须有一个且只有一个 View 类型的参数。

  这种方法在一些新项目中不是很常见,在一些老的Android项目中可能会有如此大量的使用。

  □通过注解绑定*敏*感*词*器

  目前,很多第三方库都提供了类似的功能。以 ButterKnife 为例:

  @OnClick({R2.id.butterknife})

  public void butterKnifeButtonOnClick(View view) {

  //do something

  }

  首先定义一个方法,该方法只有一个View类型参数,然后在方法上使用ButterKnife的@OnClick注解声明,其中参数表示控件的android:id。

  这种方法也是比较流行的使用方法之一。

  ButterKnife 更详细的使用方法请参考其官网:。

  □listener 收录 Lambda 语法

  Java 8 支持 Lambda,例如:

  AppCompatButton 按钮 = findViewById(R.id.lamdbaButton);

  button.setOnClickListener(view ->Log.i("MainActivity", "Lambda OnClick"));

  这种方式也是目前比较流行的一种使用方式。

  其实这根本就不是绑定*敏*感*词*器的方式,而是绑定的*敏*感*词*器收录了Lambda语法。之所以在这里提到,是因为这种方法在我们选择全埋点方案时会产生一定的影响。比如后面要介绍的AspectJ全埋点方案就不能支持这种使用Lambda语法的点击。事件。

  Lambda 的详细信息请参考:。

  □ 通过 DataBinding 绑定*敏*感*词*器

  首先在布局文件中声明 android:onClick 属性:

  

  android:onClick属性值为“@{handlers::dataBindingOnClick}”,表示按钮的点击处理逻辑是handlers对象的dataBindingOnClick方法,其中handlers对象是MainActivity的一个实例。

  然后在对应的Java文件中声明android:onClick属性指定的方法dataBindingOnClick:

  公共无效数据绑定OnClick(查看视图){

  //do something

  }

  注意:此方法必须有一个且只有一个 View 类型的参数。

  这种方法也是一种新的流行的使用方式。

  DataBinding更详细的使用方法请参考官网:data-binding/index.html。

  由于整点的重点是解决控件的点击行为数据,了解控件可以设置哪些*敏*感*词*器,以及设置或绑定*敏*感*词*器的不同方式,对我们学习或选择会有​​很大帮助全点的解决方案。.

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线