---
Android 全埋点
---

#### 目录

1. 思维导图

2. 埋点概述

   - 事件采集
     - $AppStart / End 事件
     - $AppViewScreen 事件
     - $AppClick 事件
   - 整体思路
     - 自动拦截参考 Android 对 View 事件的处理逻辑
     - 自动插入参考编译器对 Java 代码的处理逻辑
   - 实现方式分类
     - 静态代理
       - AspectJ
       - ASM
       - javassist
       - AST
     - 动态代理
       - 代理 View.OnClickListener
       - 代理 Window.Callback
       - 代理 View.AccessibilityDelegate

   - 手动埋点
   - 自动化埋点

3. $AppViewScreen 全埋点实现

4. $AppStart / End 全埋点实现

5. $AppClick 全埋点实现

6. 参考

#### 思维导图

#### 埋点概述

##### 事件采集

埋点就是预先手机用户的所有行为数据，采集的事件主要包括以下三种：

1. $AppStart / End 事件

   指 App 启动以及 App 退出，启动包括冷启动和热启动，退出包括正常退出、退出后台以及 App 崩溃等。

2. $AppViewScreen 事件

   指 App 页面浏览，对于 Android 来说就是指 切换 Activity。

3. $AppClick 事件

   指 App 控件被电击。

采集的这三种事件中，最重要也最难采集的就是 $AppClick 点击事件，全埋点基本上也是围绕如何采集 $AppClick 事件。

##### 整体思路

全埋点的整体思路，就是找到那个被点击的 View 的原处理逻辑，然后利用一定的技术原理，在原处理逻辑进行拦截，或在其中插入相应的埋点代码，从而达到自动埋点的效果。

至于如何做到**自动拦截**原点击事件的处理逻辑，一般参考 Android 系统 View 事件处理逻辑来进行的。

至于如何做到**自动插入**埋点代码，基本上都是参考编译器对 Java 代码的处理流程来进行的。即：

```java
javaCode --> .java --> .class --> .dex
```

##### 实现方式分类

按照在 “ 什么时候去代理或者插入代码 ” 这个条件来区分的话，Android 全埋点技术可以分为两种方式：

1. 静态代理

   静态代理就是通过 Gradle Plugin 在编译期间 “ 插入 ” 或者修改代码（ .class 文件 ）。比如 AspectJ、ASM、javassist、AST 等方案均属于这种方式，这几种方式的处理时机可以参考下图：

   ![](https://i.loli.net/2018/12/11/5c0fd3b1b7a2f.jpg) 

2. 动态代理

   动态代理即指在代码运行的时候去进行代理，比如常见的代理 View.OnClickListener、Window.Callback、View.AccessibilityDelegate 等方案均属于动态代理。

显然，静态代理明显优于动态代理。

#### $AppViewScreen 全埋点实现

页面浏览事件的监听还是很简单的，即 Application#ActivityLifecycleCallbacks，从 API 14 开始提供，它是 Application 的一个内部接口，提供了一系列回调方法，用于让开发者可以对 Activity 的所有生命周期事件进行集中处理（或者叫监控）。

需要注意的是，Android 是先回调接口函数，再去执行 Activity 本身的生命周期函数。

```java
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        this.registerActivityLifecycleCallbacks(new MyActivityLifecycleCallback());
    }

    static class MyActivityLifecycleCallback implements ActivityLifecycleCallbacks{

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        }

        @Override
        public void onActivityStarted(Activity activity) {
        }

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityStopped(Activity activity) {
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
        }
    }
}
```

#### $AppStart / End 全埋点实现

对于 App 的启动和退出事件，其实就是判断当前 App 是出于前台还是处于后台。而 Android 系统本身没有给 App 提供相关的接口来判断这些状态，所以只能借助其他方式来间接判断。

| 方案   | 含义                       | 是否需要权限 | 特点             |
| ------ | -------------------------- | ------------ | ---------------- |
| 方案一 | RunningTask                | 否           | 5.0 此方法已废弃 |
| 方案二 | RunningProcess             | 否           | 无               |
| 方案三 | ActivityLifecycleCallbacks | 否           | 简单、代码量少   |
| 方案四 | UsageStatsManager          | 是           | 需要用户手动授权 |
| 方案五 | 无障碍服务                 | 否           | 需要用户手动授权 |
| 方案六 | 读取 /proc 目录下的信息    | 是           | 效率             |

详见：[埋点之应用前后台判断](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/%E5%9F%8B%E7%82%B9/%E5%9F%8B%E7%82%B9%E4%B9%8B%E5%BA%94%E7%94%A8%E5%89%8D%E5%90%8E%E5%8F%B0%E5%88%A4%E6%96%AD.md)

#### $AppClick 全埋点实现

[AspectJ 实现方式](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/%E5%9F%8B%E7%82%B9/AOP_AspectJ.md)

[代理 View.OnClickListener](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/%E5%9F%8B%E7%82%B9/%E5%9F%8B%E7%82%B9%E4%B9%8B%E4%BB%A3%E7%90%86%20View.OnClickListener.md)

#### 参考

《神策数据 Android 全埋点技术白皮书》

[https://github.com/wenmingvs/AndroidProcess](https://github.com/wenmingvs/AndroidProcess)

