埋点模块是一个完整的系统不可获取的一部分,现在也有很多第三方的埋点SDK:

1、友盟、百度统计

优点:灵活、准确,可以定制化。
缺点:业务埋点量非常大,开发成本高,不易维护,如果要修改、新增埋点,需要重新发版。

2、编译时字节码插桩埋点

实现方案:利用Gradle插件,在编译阶段在代码中插入埋点代码,进行数据采集。
代表方案:GrowingIO SDK集成文档
优点:开发效率高,无需手动埋点,编译时插入代码,性能高,支持数据可回溯。
缺点:埋点灵活性低。

像友盟、百度统计这样耦合性太大,虽然可通过二次封装的方式,降低对这些SDK的依赖,但埋点统计模块耦合性仍然很大,为了解决这个问题,我们可通过无埋点方案来实现数据的收集过程。
无埋点方式是通过全局监听或AOP技术添加埋点的一种实现方案,开发者不需要在每个需要埋点的地方添加代码,只需要根据服务器分发的配置,获取相应的埋点数据即可。一方面代码耦合度低,同时灵活度也高,埋点数据直接由服务器控制。缺点就是没有侵入式埋点精准。

Android无埋点 GrowingIO SDK集成

1、.添加依赖代码

apply plugin: 'com.android.application'
//添加 GrowingIO 插件
apply plugin: 'com.growingio.android'
android {    defaultConfig {        resValue("string", "growingio_project_id", "您的项目ID")       resValue("string", "growingio_url_scheme", "您的URL Scheme")    }
}
dependencies {    //GrowingIO 无埋点 SDK    implementation 'com.growingio.android:vds-android-agent:autotrack-2.8.7'
}

2、添加 URL Scheme 和应用权限

URL Scheme 是您在 GrowingIO 平台创建应用时生成的该应用的唯一标识。把 URL Scheme 添加到您的项目,以便我们唤醒您的应用。
将应用的 URLScheme 和应用权限添加到你的 AndroidManifest.xml 中的 LAUNCHER Activity 下。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.growingio.testdemo">        <!-- GIO 需要的权限 -->    <uses-permission android:name="android.permission.INTERNET" />    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />    <uses-permission android:name="android.permission.READ_PHONE_STATE" />    <!-- GIO 需要的权限 -->        <!--请注意<application/>标签中的name属性值(这里为android:name=".MyApplication")必须为您的Application类-->    <application        android:name=".MyApplication"        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />               <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>           <!--请添加这里的整个 intent-filter 区块,并确保其中只有一个 data 字段-->          <intent-filter>               <data android:scheme="growing.您的URL Scheme" />               <action android:name="android.intent.action.VIEW" />               <category android:name="android.intent.category.DEFAULT" />               <category android:name="android.intent.category.BROWSABLE" />            </intent-filter>           <!--请添加这里的整个 intent-filter 区块,并确保其中只有一个 data 字段-->       </activity>    </application>
</manifest>

3、. SDK初始化配置
SDK的初始化时机必须在 Application 的 onCreate 方法中进行。
请将 GrowingIO.startWithConfiguration 加在您的 Application 的 onCreate 方法中

public class MyApplication extends Application {    @Override    public void onCreate() {        super.onCreate();        GrowingIO.startWithConfiguration(this, new Configuration()            .trackAllFragments()            // 建议使用 BuildConfig 设置            .setChannel("XXX应用商店")       );    }
}

注意事项
trackAllFragments方法如果在初始化时调用,APP 内部的 Fragment 将代替 Activity 作为页面,一个 Activity 中包含多个 Fragment 时大概率会是面积最大的 Fragment 作为当前的页面事件( Page )。

2.重要配置

1、自定义点击事件

如果您有自定义的控件重写了View的onTouchEvent方法来判断和处理点击事件,那么必须调用它的PerformClick,并且设置相应的onClickListener。

2、设置页面别名

对于安卓应用,页面指的是Activity或者Fragment。
有些时候,对于完成某个功能的页面,统计时可能需要进一步细分。 比如,对于展示商品列表的页面,需要区分衣物类商品,以及食品类商品的两种列表的访问量。
为处理这种场景,我们提供了取别名的方法来区分这两种情况下的页面,方法如下:
GrowingIO.setPageName(Activity activity, String name)
如果您设置的的对象是Fragment,将上方的Activity替换为Fragment即可。

我们用Activity来举例,具体说明它的用法。
某个应用的商品列表页是用FeedActivity实现的,所以默认的页面名称都是FeedActivity。
现在我们想区分衣物类商品列表和食品类商品列表,分别看它们的浏览量,可以在onCreate方法中添加如下代码:

public class FeedActivity extends Activity {         @Override         protected void onCreate(Bundle savedInstanceState) {                 super.onCreate(savedInstanceState);                 GrowingIO.getInstance().setPageName(this, "Clothing");     }
}

必须在该Activity的onCreate方法中完成该属性的赋值操作。页面别名建议设置为字母、数字和下划线的组合。
为查看数据方便,请尽量对 iOS 和安卓的同功能页面取不同的名称。

3.自定义数据上传

除上述的用户行为数据(无埋点数据)之外,GrowingIO 还提供了多种 API 接口,供您上传一些自定义的数据指标及维度
请参考Android SDK API > 自定义数据上传API
集成了 GrowingIO 的 SDK 之后,它将会自动地为您采集一系列用户行为数据,进行数据分析。除自动收集的用户行为数据,GrowingIO 还提供了多种 API 接口,供您上传一些自定义事件和变量,下面介绍自定义事件和变量 API 使用方法,后文简称埋点事件API
1、设置登录用户ID(setUserId)

GrowingIO.getInstance().setUserId(String userId);

2、. 清除登录用户ID(clearUserId)

GrowingIO.getInstance().clearUserId();

3、设置登录用户变量(setPeopleVariable)

GrowingIO gio = GrowingIO.getInstance();
gio.setPeopleVariable(String key, String value);
gio.setPeopleVariable(String key, Number value);
gio.setPeopleVariable(String key, Boolean value);
gio.setPeopleVariable(JSONObject peopleVariables);

4、设置访问用户变量(setVisitor)

GrowingIO.getInstance().setVisitor(JSONObject visitorVar)

5、设置页面级变量(setPageVariable)
发送页面级别的信息,在添加代码之前必须在打点管理界面上声明页面级变量。

GrowingIO gio = GrowingIO.getInstance();
gio.setPageVariable(Activity activity, String key, String value);
gio.setPageVariable(Activity activity, String key, Number value);
gio.setPageVariable(Activity activity, String key, Boolean value);
gio.setPageVariable(Activity activity, JSONObject pageLevelVariables);gio.setPageVariable(Fragment fragment, String key, String value);
gio.setPageVariable(Fragment fragment, String key, Number value);
gio.setPageVariable(Fragment fragment, String key, Boolean value);
gio.setPageVariable(Fragment fragment, JSONObject pageLevelVariables);// SDK 2.6.6 以上支持 androidx, 增加接口:
gio.setPageVariableX(Fragment fragment, String key, String value);
gio.setPageVariableX(Fragment fragment, String key, Number value);
gio.setPageVariableX(Fragment fragment, String key, Boolean value);
gio.setPageVariableX(Fragment fragment, JSONObject pageLevelVariables);

4、创建应用

添加代码之后,请先Clean项目,然后再进行编译,并在你的 Android App 安装了 SDK 后重新启动几次 App,保证行为采集数据自动发送给 GrowingIO,以便顺利完成检测。
在GrowingIO平台的应用创建页面继续完成应用创建的数据检测,检测成功后应用创建成功。

5、验证SDK是否正常采集数据

GrowingIO为您提供多种验证SDK是否正常采集数据的方式:
方式一: Mobile Debugger
方式二:在SDK中设置了Debug模式后,在IDE编译器控制台查看数据采集日志。
方式三:(推荐) 数据校验

58无埋点数据采集技术在Android端实践

WMDA SDK Android端整体架构主要分为圈选模块、事件采集上报、配置管理三部分

下面根据事件采集上报流程分别来介绍事件采集、处理、存储、上报和圈选

1、 事件采集

WMDA移动端数据采集类型主要分三种:页面浏览事件、控件点击事件和自定义事件。作为无埋点解决方案,SDK核心点就是事件的无痕采集。 其中,这三种事件又对应不同的采集处理方式,WMDA通过不同的技术方案进行采集,最后将事件统一处理,然后存储、上报。

1.1、 插桩入口

事件采集是无埋点技术的核心,其中WMDA对Fragment和控件点击事件拦截,使用的是自己开发的gradle插件wmda plugin,编译时使用ASM以字节码插桩的方式注入代码,实现事件的采集。

对于事件拦截,首先需要确定插入时机和待修改字节码文件。这里我们使用Transform API作为插桩入口,在Java Compiler之后,class文件打包成dex文件之前修改字节码文件。由于Transform API是在Gradle插件版本1.5.0出现的,所以项目开发中Gradle插件版本不能低于1.5.0。

classpath 'com.android.tools.build:gradle:1.5.0'

然后在transform中遍历并操作字节码文件,因为现在很多大型项目,都会进行组件化操作,拆分成多个Library。所以这里除了要修改我们的应用源码,还需要对第三方库中的字节码文件进行扫描操作

void transform(Context context, Collection<TransformInput> inputs, Collection<TransformInput> referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {inputs.each { TransformInput input ->input.directoryInputs.each {DirectoryInput directoryInput ->//对应用源码生成的class操作}input.jarInputs.each { JarInput jarInput ->//对第三方库中class操作}}
}

1.2、页面事件

针对页面浏览事件,WMDA分两种不同的方式进行采集。
Activity采集 针对Activity,WMDA采用的方式是使用LifecycleCallback来监听页面的开启和关闭。 在页面开启时,拦截生命周期方法onResume,然后在事件处理模块处理,将其格式化成事件结构,并进行存储上报。

public void onActivityResumed(Activity activity) {// 页面浏览事件采集处理PageManager.getInstance().onActivityResumed(activity);
}

Fragment采集 针对Fragment,由于Android系统并没有关于Fragment生命周期的回调监听,所以这里WMDA通过Gradle插件,在编译时期,利用ASM库进行字节码操作,对Fragment注入WmdaAgent相应的页面采集方法,完成事件采集。在注入策略上,我们只需要对Fragment父类为下面两个的页面注入采集代码即可。

android/app/Fragment
android/support/v4/app/Fragment

对Fragment相关方法注入代码示例:

MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);MethodVisitor wrappedMv = mv;if (wrappedMv != null) {// 在onResume中插入WmdaAgent.onFragmentResumed方法if (name.equals("onResume") && desc.equals("()V")) {wrappedMv.visitCode()wrappedMv.visitVarInsn(Opcodes.ALOAD, 0)wrappedMv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/wuba/wmda/autobury/WmdaAgent","onFragmentResumed", "(Landroid/app/Fragment;)V")}}
}

WmdaAgent代码:

public static void onFragmentResumed(Fragment fragment) {// 页面浏览事件采集处理PageManager.getInstance().onFragmentResumed(fragment);
}

控件点击事件
关于点击事件的采集,使用Gradle插件在编译时埋点可以完美继承Mixpanel方案的各项优点,同时又可以规避其性能、数据准确性和版本适配问题。于是,在控件点击事件的采集上,我们调整了技术实现方案,从动态对View设置代理演进为编译时插入埋点代码。

WMDA对点击事件拦截支持常用的一些第三方框架,比如:

ButterKnife、databinding、AndroidAnnotations、RxBinding

具体的技术和之前的Fragment插桩埋点是一样的,编译时对onClick方法注入代码,以AOP方式对事件拦截处理。核心实现思路如下:

MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);MethodVisitor wrappedMv = mv;if (wrappedMv != null) {// 查找出方法名为onClick,入参为View的方法,注入WmdaAgent.onViewClick(View view)if (name.equals("onClick") && desc.equals("(Landroid/view/View;)V")) {wrappedMv.visitCode()wrappedMv.visitVarInsn(ALOAD, 1);wrappedMv.visitMethodInsn(INVOKESTATIC, "com/wuba/wmda/autobury/WmdaAgent", "onViewClick", "(Landroid/view/View;)V", false);}}
}

自定义事件

无埋点是WMDA的核心功能,但是由于业务场景特点,无埋点并不能完全满足所有业务场景需求,所以WMDA也提供了对手动埋点支持,使得WMDA在实际的使用中更加灵活,数据统计也更全面。

2、事件处理

事件收集完成之后,就会发送到事件处理线程,对原生的事件进行加工,我们将页面的class全路径作为页面的特征值。APP_PAGE示例:

com.wuba.wmdademo.MainActivity

采集到页面事件后将其传入子线程处理,然后再提取出业务开发人员在页面onCreate方法中设置的页面ID及页面自定义属性,将这些数据统一格式化,构造成页面浏览事件,传给事件存储模块。

在对控件事件处理中,我们面临一个最大的问题就是,如何区分每一个控件,即如何定义控件的特征值。在这里,我们借鉴了Mixpanel的方法,即将View自身的类名及index,以及其逐级向上的所有父View的类名和index统一收集起来,然后将所有遍历信息拼接起来,当做该View在当前页面的唯一特征值。控件的唯一标识:页面APP_PAGE + ViewPath + index

ViewPath举例:

/MainWindow/LinearLayout[0]/FrameLayout[1]/ActionBarOverlayLayout[0]#decor_content_parent/ContentFrameLayout[0]/LinearLayout[0]/ScrollView[0]/LinearLayout[0]/AppCompatButton

3、 事件存储

事件处理完成之后,要交由存储模块进行本地持久化。在存储之前,先会检查存储策略,满足策略后再进行存储。

存储这里,使用的是本地SQLite存储Protobuf实例的二进制,然后使用AES-256进行加密存储。

4、事件上报

事件存储完成之后,会触发上报请求。在上报之前,WMDA会先检查上报策略,满足策略后进行上报。

上报这里为了缩小WMDA包,只使用了HttpUrlConnection来处理网络操作。在数据上报的时候使用了GZIP+ProtoBuf来减少流量消耗,保证收集数据的同时,提升用户体验。

5、圈选模块

之前只是介绍数据采集方案,数据全量采集上报后,并不会直接分析处理,还需要一个圈选指标的过程。关于圈选的介绍,大家可以查看数据驱动增长:58无埋点用户行为分析实践之路这篇的圈选部分,这里就不做重复介绍了。

通常,我们圈选时会在一个页面停留较长时间,这时其实是不需要一直将当前页面快照数据发送给服务端的,因为页面并没有变化。这块有一个优化策略,SDK会根据当前屏幕快照生成一指纹,只有当前屏幕有变更时才会将当前页面快照数据发给用户分析平台。

总结-----现存问题

当然,现阶段无埋点技术采用的字节码插桩方案还是存在一些短板,需要我们后续探索和解决。

1、click监听如果是在layout中使用android:onClick="xxxMethod"设置的暂时无法进行采集。这个设置监听的方法是利用Java的反射原理,去寻找对应的Method,在WMDA中是通过拦截OnClickListener点击事件来进行监听的,因此无法实现监听。
2、由于目前采用的是编译时插入埋点,所以不支持目前比较流行的RN框架。
3、同样因为是编译时插入埋点,所以对热更新的补丁支持可能也不到位。

58无埋点数据采集参考文章路径:https://cloud.tencent.com/developer/article/1164333

数据埋点几种方式介绍相关推荐

  1. android 定位的几种方式介绍

    [地理位置] android 定位的几种方式介绍 开发中对于地图及地理位置的定位是我们经常要用地,地图功能的使用使得我们应用功能更加完善,下面 www.androidkaifa.com 总结了一下网络 ...

  2. (转)基于MVC4+EasyUI的Web开发框架经验总结(12)--利用Jquery处理数据交互的几种方式...

    http://www.cnblogs.com/wuhuacong/p/4085682.html 在基于MVC4+EasyUI的Web开发框架里面,大量采用了Jquery的方法,对数据进行请求或者提交, ...

  3. STM32芯片烧录的三种方式介绍,串口、STM32 ST-LINK Utility以及STM32CubeProgrammer

    STM32芯片烧录的三种方式介绍,串口.STM32 ST-LINK Utility以及STM32CubeProgrammer 1 概述 1.1资源概述 1.2 STM32串口烧录方式 2.KEIL软件 ...

  4. 数据迁移的几种方式 - MySQL数据库

    写在前面:博主是一只经过实战开发历练后投身培训事业的"小山猪",昵称取自动画片<狮子王>中的"彭彭",总是以乐观.积极的心态对待周边的事物.本人的技 ...

  5. docker容器运行mysql持久化_docker容器实现数据持久化的两种方式及其区别

    前言 这篇博文是我对docker实现数据持久化几种方式的特征进行一个总结. 在docker中,它的存储文件系统是在dockerhost上原有的xfs或ext4架设了一层文件系统:overlay2(将此 ...

  6. css样式引入形式php,引入css样式表的四种方式介绍

    一.使用STYLE属性: 将STYLE属性直接加在个别的元件标签里. 这种用法的优点 是可灵巧应用样式於各标签中,但是缺点则是没有整篇文件的『统一性』. 二.使用STYLE标签: 将样式规则写在标签之 ...

  7. python的命名空间_python中命名空间的三种方式介绍(附示例)

    本篇文章给大家带来的内容是关于python中命名空间的三种方式介绍(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. python中的命名空间分三种: 内置的命名空间,在启动解 ...

  8. linux数据同步技术比较,linux下实现web数据同步的四种方式(性能比较)教程.docx

    linux下实现web数据同步的四种方式(性能比较)教程 实现web数据同步的四种方式=======================================1.nfs实现web数据共享2.rs ...

  9. Hive数据导出的几种方式

    Hive数据导出的几种方式 参考资料地址:http://blog.csdn.net/qianshangding0708/article/details/50394789 感谢分享 (1)导出到本地文件 ...

最新文章

  1. Schnorr签名与比特币隐私的必然性
  2. 根据文件路径生成相应文件
  3. jvm性能调优实战 -51修复栈内存区域内存溢出问题 StackOverFlow
  4. Android官方网站
  5. 第 2 章:初出茅庐【初级篇 - 2.1 穷竭搜索】
  6. WPF实现用户头像裁剪
  7. MacOS 如何安装 SVN 命令终端
  8. ssm框架使用重定向报404_如何在 ASP.NET Core MVC 中处理 404 错误
  9. 关于Lodop打印控件
  10. 面向对象chapter10
  11. 如何利用Webp和http缓存节省30%的网络流量
  12. Monkey test 汇总
  13. 【开源微信】Java实现基于Redis公众号模板消息队列
  14. 2021年PMP考试模拟题1(含答案)
  15. tortoisegit 小乌龟的使用
  16. RN实现手机摇一摇功能
  17. 永恒之蓝勒索漏洞复现
  18. 汉罗塔问题和杨辉三角(java实现)
  19. 网站建设需要网站服务器吗
  20. 数据库课程设计:图书借阅系统(Java+MySQL)

热门文章

  1. 如何查看centos7系统的服务器ip地址
  2. 手机实现远程开机功能
  3. 订阅发布功能Java实现
  4. c语言中用户验证程序,C语言用户登录系统账户密码比对
  5. 网格搜索(调参)与数据预处理
  6. 愿你一生欢喜,不为世俗裹挟 | 笔记摘要
  7. yolov2中的reorg网络层数据理解
  8. 函数的节流(throttle)
  9. Mysql添加新用户设置密码
  10. dmesg 命令详解