基本流程
- 根據(jù)需求確定要hook的對象
- 尋找要hook的對象的持有者尖殃,拿到要hook的對象
- 定義要hook的對象的代理類,并且創(chuàng)建該類的對象
- 使用上一步創(chuàng)建出來的對象,替換掉要hook的對象
示例代碼
定義一個工具類用來hook
public class HookManager {
@SuppressLint({"DiscouragedPrivateApi", "PrivateApi"})
public static void hook(Context context, final View view) {
try {
//1.反射執(zhí)行View類的getListenerInfo()方法,拿到View的ListenerInfo對象柒瓣,這個對象就是點(diǎn)擊事件的持有者
Method method = View.class.getDeclaredMethod("getListenerInfo");
method.setAccessible(true);//由于getListenerInfo()方法并不是public的,所以要加這個代碼來保證訪問權(quán)限
Object listenerInfo = method.invoke(view);
//2.取得真實(shí)的mOnClickListener對象
Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");//這是內(nèi)部類的表示方法
Field field = listenerInfoClz.getDeclaredField("mOnClickListener");
final View.OnClickListener clickListener = (View.OnClickListener) field.get(listenerInfo);
//3.創(chuàng)建我們自己的點(diǎn)擊事件代理類
//方式1:自己創(chuàng)建代理類
// ProxyOnClickListener proxyOnClickListener = new ProxyOnClickListener(clickListener);
//方式2:由于View.OnClickListener是一個接口吠架,所以可以直接用動態(tài)代理模式
//Proxy.newProxyInstance的3個參數(shù)依次分別是:
//本地的類加載器
//代理類的對象所繼承的接口(用Class數(shù)組表示芙贫,支持多個接口)
//代理類的實(shí)際邏輯,封裝在new出來的InvocationHandler內(nèi)
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", "點(diǎn)擊事件被hook到了");//加入自己的邏輯
return method.invoke(clickListener, args);//執(zhí)行被代理的對象的邏輯
}
});
//4.用我們自己的點(diǎn)擊事件代理類傍药,設(shè)置到"持有者"中
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", "點(diǎn)擊事件被hook到了");//加入自己的邏輯
if (clickListener != null) {
clickListener.onClick(v);//執(zhí)行被代理的對象的邏輯
}
}
}
}
給對應(yīng)的View設(shè)置點(diǎn)擊事件磺平,并hook這個View,這樣就能在View被點(diǎn)擊時加入自己的邏輯
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("yzt", "點(diǎn)擊事件");
}
});
HookManager.hook(this, tv);
這里就通過打印信息來演示
2021-06-25 14:09:02.225 23154-23154/com.yzt.hookdemo E/yzt: 點(diǎn)擊事件被hook到了
2021-06-25 14:09:02.225 23154-23154/com.yzt.hookdemo E/yzt: 點(diǎn)擊事件