在App開發過程中,埋點都要花費很多時間,而且還有遺漏沒有埋的情況,能不能做到伺服器下發哪些控件要埋,然後用戶端自動埋。 前幾天想出來了一個方案,今天實作了一個Demo,大概過程就是:周遊界面上所有View --> 如果View設定了OnClickListener,并且View的ID在配置檔案中,那就替換OnClickListener為DelegentOnClickListener -->DelegentOnClickListener先調用埋點,然後再調用原先View的ClickListener。
1.首先擷取到控件的OnClickListener,Android 系統中沒有相對應的API,檢視View的源碼,發現可以通過反射的方式擷取到,Android系統将View相關Listener放到了一個内部類ListenerInfo中,從View 的mListenerInfo中可以擷取到OnClickListener,大概的代碼是這樣的:
private View.OnClickListener getOnClickListener(View view) {
boolean hasOnClick = view.hasOnClickListeners();
if (hasOnClick) {
try {
Method method = View.class.getDeclaredMethod("getListenerInfo", null);
if (null != method) {
method.setAccessible(true);
Object object = method.invoke(view, new Object[]{});
if (null != object) {
Class listenerInfoClazz = object.getClass();
Field mOnClickListener = listenerInfoClazz.getDeclaredField("mOnClickListener");
if (null != mOnClickListener) {
mOnClickListener.setAccessible(true);
Object listener = mOnClickListener.get(object);
if (null != listener && listener instanceof View.OnClickListener) {
return (View.OnClickListener) listener;
}
}
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
return null;
}
2.周遊界面中的所有View,然後替換在配置檔案中的View的OnClickListener,實作代碼:
private void setClickListener(final View view) {
if (null != view) {
boolean hasOnClick = view.hasOnClickListeners();
if (hasOnClick) {
final View.OnClickListener listener = getOnClickListener(view);
int id = view.getId();
String name = getResources().getResourceName(id);
//AdapterView不能設定OnClickListener
if (!(view instanceof AdapterView)) {
//檢查是否在配置檔案中,請求OnClickListener沒替換過
if (!(listener instanceof DelegentOnClickListener) &&
mTraceManager.isNeedTrace(this.getClass().getName(), name)) {
view.setOnClickListener(new DelegentOnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "new clickListener");
if (null != listener) {
listener.onClick(v);
}
Log.e(TAG, v.getTag(v.getId()) + "");
}
});
}
}
}
if (view instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup) view;
int childCount = viewGroup.getChildCount();
if (childCount > 0) {
for (int i = 0; i < childCount; i++) {
View childView = viewGroup.getChildAt(i);
setClickListener(childView);
}
}
}
}
}
3.替換界面中所有的View的OnClickListener,實作代碼: setClickListener(findViewById(android.R.id.content));
4.在Assets的配置檔案中添加要追蹤的Activity和View id。 這個Demo實作了大概的原理驗證,距離真實的産品,還有很大的距離,包括替換所有View的OnClickListener的時機怎麼選擇,ListView,RecycleView的Item監聽,還有OnTouchListenr,OnLongClickListener實作,通過設定DelegentTouchListener應該還可以實作界面的熱力圖,配置檔案可以從伺服器端下發,做到動态埋點,View可以有意識的setTag(id,object),然後埋點反序列化這個object,可以做到埋入一些參數資訊,配置檔案還可以擴充一下,有控件名稱之類的。 配圖很夢幻,如果實作了自動埋點,對營運和開發來說也是夢幻的。
項目位址:https://github.com/fengcunhan/AutoTrace
版權聲明:本文為CSDN部落客「weixin_33696822」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/weixin_33696822/article/details/92447452