天天看點

Android自動埋點的一種實作

在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