为什么使用hook?
有时系统源码无法实现我的需求
有时我的需求A写法太麻烦了,效果也不要好。但是用B写法,稍微改动点源码即可完美实现
N多理由
如之前写的LiveDataBus也用到了hook
hook的前提:清晰的步骤流程、反射、熟悉源码
刚好前段时间重新梳理了触碰流程
那就hook个点击事件
一、反射
先介绍反射中重要、常用的方法
参考https://www.jianshu.com/p/9be58ee20dee
获取class
val c = View::class.java
val cc = Class.forName("android.view.View")
//内部类
val ccc = Class.forName("android.view.View\$ListenerInfo")
//只能够调用无参的构造函数,即默认的构造函数;
c.newInstance() 此处只是说明,view无空的构造方法
内部类 java写法:android.view.View$ListenerInfo 但是在kotlin中$是一个函数,所以需要加 \ 防止转义
获取class中的构造方法
//getDeclaredConstructor可以返回指定参数的构造函数,而getConstructor只能返回指定参数的public的构造函数
//此处只是为了展示方法,view没有空的构造方法
val con = View::class.java.getConstructor()
val con1 = View::class.java.getConstructor(Context::class.java)
val con2 = View::class.java.getDeclaredConstructor()
con1.newInstance(this)
获取class中的方法
val m = View::class.java.getDeclaredMethod("方法名")
//对应View中的m方法,形参string fun m(aa:String){}
val m1 = View::class.java.getDeclaredMethod("m",String::class.java)
val ms = View::class.java.declaredMethods
//方法执行
//a.invoke(b,c) == b对象执行a方法 c为a方法的形参
//invoke(调用底层方法的对象,用于方法调用的参数)
//如上面的m1 则是
//m1.invoke(mTextView,"String形参")
获取class的成员变量
来修改View中的mUserPaddingRight mTextView为简单的TextView
val f = View::class.java.getDeclaredField("mUserPaddingRight")
//参数or方法 设置为public
f.isAccessible = true
//从mTextView中提取mUserPaddingRight 属性值
val o = f.get(mTextView) as Int
Log.e("mUserPaddingRight1",o.toString())
f.isAccessible = true
f.set(mTextView,100)
val oo = f.get(mTextView) as Int
Log.e("mUserPaddingRight2",oo.toString())
打印结果:0和100
二、明确需求、理清步骤
目标:Hook一个点击事件
源码:上面有链接。
思路:setOnClickListener() 设置的点击事件是在View中的内部类ListenerInfo中的mOnClickListener
1、获取View中的ListenerInfo
2、获取ListenerInfo中的mOnClickListener
3、增加或替换点击事件
class MainActivity : AppCompatActivity() {
private var mTextView:TextView ?= null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mTextView = findViewById<TextView>(R.id.tvFlag)
mTextView?.setOnClickListener {
Log.e("onClick","请猛搓")
Toast.makeText(this@MainActivity,"请猛搓",Toast.LENGTH_LONG).show()
}
hook()
}
fun hook(){
//获取View中的getListenerInfo方法
val getListenerInfoMethod = View::class.java.getDeclaredMethod("getListenerInfo")
//getListenerInfo方法默认没有修饰符 设置为public
getListenerInfoMethod.isAccessible = true
//mTextView执行getListenerInfoMethod代表的方法
val mListenerInfo = getListenerInfoMethod.invoke(mTextView)
//获取listenerInfo内部类
val listenerInfoClass = Class.forName("android.view.View\$ListenerInfo")
//获取listenerInfo内部类中的mOnClickListener
val fieldObservers = listenerInfoClass.getDeclaredField("mOnClickListener")
//从mTextView中的mListenerInfo中 提取出mOnClickListener 如果替换点击事件则不需要此步骤
val mOnClickListener = fieldObservers.get(mListenerInfo) as View.OnClickListener
fieldObservers.set(mListenerInfo, MyOnClick(mOnClickListener))
}
class MyOnClick(private val onClickListener: View.OnClickListener): View.OnClickListener{
override fun onClick(v: View?) {
Log.e("hook","hook到了点击事件哦")
//onClickListener.onClick(v) //看你心情是否注释 替换或增加
}
}
}
OnClickListener 是个接口,接口我会想到mvp,想到面向接口编程,然后想到代理模式
上面只是简单的用静态代理来处理,也可以用动态代理才处理
详情百度
很多技术可能现在学习了,但是实际项目用的少,过段时间忘记了
很多技术常用,然而不知道是属于什么设计模式,为什么会这样用
不强求,但是起码在需要的时候,知道有这么个东西