无痕埋点简单实现

hook + aop 简单实现

1. View的层级

1639734894(1).jpg
1.jpg

View 所有的Listener都存储在getListenerInfo()=ListenerInfo
所有hook getListenerInfo()拿到mOnClickListener代理到我们自己的WrapperOnClickListener里

1.hook setOnClickListener
   fun hookViewOnClickListener(view: View) {
        val listener = getOnClickListener(view)
        if(listener !is WrapperViewOnClickListener){
             view.setOnClickListener(WrapperViewOnClickListener(listener))
        }
 }
2.反射拿到getListenerInfo,通过ListenerInfo拿到mOnClickListener
private fun getOnClickListener(view: View): View.OnClickListener? {
        val hasOnClick = view.hasOnClickListeners()
        if (hasOnClick) {
            try {
                val viewClazz: Class<*> = View::class.java
                val method: Method = viewClazz.getDeclaredMethod("getListenerInfo")
                if (!method.isAccessible) {
                    method.isAccessible = true
                }
                val listenerInfoObj: Any = method.invoke(view)
                val listenerInfoClazz = Class.forName("android.view.View\$ListenerInfo")
                val field: Field = listenerInfoClazz.getDeclaredField("mOnClickListener")
                if (!field.isAccessible) {
                    field.isAccessible = true
                }
                return field.get(listenerInfoObj) as View.OnClickListener
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return null
    }
}

3.WrapperViewOnClickListener

  class WrapperViewOnClickListener(val listener: View.OnClickListener?) :   View.OnClickListener {
    override fun onClick(v: View?) {
        Log.e("TAG","自动埋点")
        listener?.onClick(v)
        val path = ViewPath.getPath(v, StringBuilder())
        Log.e("TAG", path)
    }
}

获取当前View所在Activity的路径

注意RecyclerView 子item的位置索引
fun getPath(view: View?, path: StringBuilder): String? {
            if (view != null) {
                if (path.isEmpty()) path.insert(0, view.javaClass.simpleName)
            } else {
                return ""
            }
            if (view.parent is ViewGroup) {
                if (view.parent is RecyclerView || view.parent is AdapterView<*>) {
                    val listView: View = view.parent as View
                    var index = 0
                    if (view.parent is RecyclerView) {
                        index = (view.parent as RecyclerView).getChildAdapterPosition(view)
                    } else if (view.parent is AdapterView<*>) {
                        index = (view.parent as AdapterView<*>).getPositionForView(view)
                    }
                  path.insert(0, view.parent.javaClass.simpleName + "[" + index + "]|")
                } else {
                   path.insert(0, view.parent.javaClass.simpleName + "[" + (view.parent as ViewGroup).indexOfChild(view) + "]|")
                }
            }
            //遇到PhoneWindow退出递归
            return if (view.parent is View) {
                //不统计统计DecorView遇到Decorview退出递归 
                if ((view.parent as View).id === android.R.id.content) {
                    path.insert(0, view.parent.javaClass.simpleName + "|")
                    path.toString()
                } else {
                    getPath(view.parent as View, path)
                }
            } else {
                path.toString()
            }
        }

效果:

TAG: 自动埋点
TAG: DecorView[0]|LinearLayout[1]|FrameLayout[0]|FitWindowsLinearLayout[1]|ContentFrameLayout[0]|LinearLayout[4]|FrameLayout[0]|FrameLayout[0]|FrameLayout[0]|FrameLayout[0]|FrameLayout[0]|FrameLayout[0]|FrameLayout[0]|AppCompatButton
or
ContentFrameLayout|ContentFrameLayout[0]|LinearLayout[4]|AppCompatButton




    
        
            
                
                    
                        
                            
                                

你可能感兴趣的:(无痕埋点简单实现)