本文参考《Android全埋点解决方案》一书,并在实操中查漏补缺。
前言
为什么选择全埋点
呢,因为传统的手动埋点
虽然简单、扩展性强,但弊端也很明显:
- 开发时间成本较高
- 改动的时间成本也较高
- 容易出现漏埋、埋错的情况
- 代码侵入性强
但也不是说全埋点就一定没有弊端,比如扩展性较差。
经过调研,实际上都是以全埋点为主
、手动埋点为辅
的情况,从而达到比较理想的埋点效果。
本文内容可能稍微有点长,但是很简单,别太长不看啊,最后有
demo源码
。
页面
一般来说我们需要的数据就是,用户在哪个页面干了什么,也就是页面和事件,现在来说页面。
原理
通过生命周期
可以计算出时长数据,以及页面对象。
Activity
页面有两个核心的需求数据:
- 浏览时长
- 页面唯一标示
这两个数据都挺好拿的,Application
有一个registerActivityLifecycleCallbacks
接口可以监测到activity的生命周期
。
有了生命周期,我们在onActivityResumed
里面记录一下开始时间,然后在onActivityPaused
中获取当前时间,就是整个页面的浏览时间
;
在生命周期方法中是有activity对象
的,这样也可以拿到全路径
作为唯一标示;
示例:
public static void registerActivityLifecycleCallbacks(Application application) { application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle bundle) { } @Override public void onActivityStarted(Activity activity) { } @Override public void onActivityResumed(Activity activity) { mBeginTime = System.currentTimeMillis(); } @Override public void onActivityPaused(Activity activity) { trackAppViewScreen(activity); } @Override public void onActivityStopped(Activity activity) { } @Override public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { } @Override public void onActivityDestroyed(Activity activity) { } }); }
在onActivityPaused
的时候我们调用了一下trackAppViewScreen
方法,并传入当前activity
,来看看trackAppViewScreen
方法。
private static void trackAppViewScreen(Activity activity) { try { if (activity == null) { return; } if (mIgnoredActivities.contains(activity.getClass().getCanonicalName())) { return; } JSONObject properties = new JSONObject(); //获取页面的参数 if (activity.toString().contains("SecondActivity")) { String userId = activity.getIntent().getStringExtra("userId"); properties.put("userId", userId); } properties.put("activity", activity.getClass().getCanonicalName()); SensorsDataAPI.getInstance().track("$AppViewScreen", properties, mBeginTime); } catch (Exception e) { e.printStackTrace(); } }
我们在trackAppViewScreen方法中创建了JSONObject对象properties,用来添加我们需要埋点的数据,比如页面的唯一标示key我们用activity表示,并取全路径作为value。
这里有一点需要注意的,我们除了可以收集一些固定参数之外,activity中intent的参数也是可以获取的,比如其他页面跳转到这个页面传的参数,我们同样可以获取到并作为埋点的参数使用的。
就像上面的SecondActivity,当MainActivity跳转到SecondActivity时传的userId是可以通过getIntent获取到的。
最后调用了SensorsDataAPI类的track方法,继续看
public void track(@NonNull String eventName, @Nullable JSONObject properties, long beginTime) { try { JSONObject jsonObject = new JSONObject(); jsonObject.put("event", eventName); // jsonObject.put("device_id", mDeviceId); JSONObject sendProperties = new JSONObject(mDeviceInfo); if (properties != null) { SensorsDataPrivate.mergeJSONObject(properties, sendProperties); } jsonObject.put("extras", sendProperties); jsonObject.put("beginTime", beginTime); jsonObject.put("endTime", System.currentTimeMillis()); jsonObject.put("pageId", SensorsDataPrivate.getCurrentActivity().getClass().getCanonicalName()); jsonObject.put("sessionId", UUID.randomUUID().toString().replace("-", "")); Log.i(TAG, SensorsDataPrivate.formatJson(jsonObject.toString())); } catch (Exception e) { e.printStackTrace(); } }
这里也很简单,先后创建了两个JSONObject,一个是最外层的jsonObject ,一个是作为参数使用的sendProperties,然后又把传过来的参数合并到sendProperties中,然后sendProperties作为extras的value使用。
endTime结束时间就取当前时间。
sessionId表示是这个埋点的唯一标示,看自己需求,非必须。
最后调用了Log打印出来,来看一下最后完整的数据:
{ "event": "$AppViewScreen", "extras": { "app_name": "TrackDemo", "screen_width": 1440, "screen_height": 2621, "app_version": "1.0", "os_version": "10", "model": "Android SDK built for x86", "manufacturer": "Google", "activity": "com.yechaoa.trackdemo.ui.MainActivity" }, "beginTime": 1603279291751, "endTime": 1603279293759, "pageId": "com.yechaoa.trackdemo.ui.MainActivity", "sessionId": "5dbb96807e634b6498f897784972ade3" }
可以看到除了我们必要的参数之外,还有一些附加参数,比如手机型号、系统版本
等等。