基本流程
- 根据需求确定要hook的对象
- 寻找要hook的对象的持有者,拿到要hook的对象
- 定义要hook的对象的代理类,并且创建该类的对象
- 使用上一步创建出来的对象,替换掉要hook的对象
示例代码
定义一个工具类用来hook
public class HookManager {
@SuppressLint({"DiscouragedPrivateApi", "PrivateApi"})
public static void hook(Context context, final View view) {
try {
//1.反射执行View类的getListenerInfo()方法,拿到View的ListenerInfo对象,这个对象就是点击事件的持有者
Method method = View.class.getDeclaredMethod("getListenerInfo");
method.setAccessible(true);//由于getListenerInfo()方法并不是public的,所以要加这个代码来保证访问权限
Object listenerInfo = method.invoke(view);
//2.取得真实的mOnClickListener对象
Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");//这是内部类的表示方法
Field field = listenerInfoClz.getDeclaredField("mOnClickListener");
final View.OnClickListener clickListener = (View.OnClickListener) field.get(listenerInfo);
//3.创建我们自己的点击事件代理类
//方式1:自己创建代理类
// ProxyOnClickListener proxyOnClickListener = new ProxyOnClickListener(clickListener);
//方式2:由于View.OnClickListener是一个接口,所以可以直接用动态代理模式
//Proxy.newProxyInstance的3个参数依次分别是:
//本地的类加载器
//代理类的对象所继承的接口(用Class数组表示,支持多个接口)
//代理类的实际逻辑,封装在new出来的InvocationHandler内
Object proxyOnClickListener = Proxy.newProxyInstance(context.getClass().getClassLoader(), new Class[]{View.OnClickListener.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("yzt", "点击事件被hook到了");//加入自己的逻辑
return method.invoke(clickListener, args);//执行被代理的对象的逻辑
}
});
//4.用我们自己的点击事件代理类,设置到"持有者"中
field.set(listenerInfo, proxyOnClickListener);
} catch (Exception e) {
e.printStackTrace();
}
}
//自定义代理类
private static class ProxyOnClickListener implements View.OnClickListener {
View.OnClickListener clickListener;
public ProxyOnClickListener(View.OnClickListener clickListener) {
this.clickListener = clickListener;
}
@Override
public void onClick(View v) {
Log.e("yzt", "点击事件被hook到了");//加入自己的逻辑
if (clickListener != null) {
clickListener.onClick(v);//执行被代理的对象的逻辑
}
}
}
}
给对应的View设置点击事件,并hook这个View,这样就能在View被点击时加入自己的逻辑
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("yzt", "点击事件");
}
});
HookManager.hook(this, tv);
这里就通过打印信息来演示
2021-06-25 14:09:02.225 23154-23154/com.yzt.hookdemo E/yzt: 点击事件被hook到了
2021-06-25 14:09:02.225 23154-23154/com.yzt.hookdemo E/yzt: 点击事件
网友评论