1.其实里面用的就是反射技术;先来看一个简单例子:
public class HookActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.tv1);
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("点击事件触发!");
Toast.makeText(HookActivity.this, "点击事件触发!", Toast.LENGTH_LONG).show();
}
});
HookClickHelper.hook(this, tv);
}
}
2.avtivity简单的调用了一个HookClickHelper的hook方法;
public class HookClickHelper {
public static void hook(final Context context, View v){
try {
Class<?> view = View.class;
Method getListenerInfo = view.getDeclaredMethod("getListenerInfo");
getListenerInfo.setAccessible(true);
//通过方法名调用invoke方法,并传入调用者对象,以及方法所需参数class;就可得到此方法获取的对象
Object mListenerInfo = getListenerInfo.invoke(v);
//内部类的调用方式
Class<?> listenerInfo = Class.forName("android.view.View$ListenerInfo");
//获取属性
Field onClickListener = listenerInfo.getDeclaredField("mOnClickListener");
//传入属性所属的对象,就可得到属性真实的值;
OnClickListener realOnClickListener = (OnClickListener) onClickListener.get(mListenerInfo);
//传入属性所属的对象,以及要给此属性设置的值,就可改变此属性原有的值
// onClickListener.set(mListenerInfo, null);
onClickListener.set(mListenerInfo, new MyOnClickListener(realOnClickListener, context));
Log.d("SetOnClickListener", "点击事件被hook到了");
} catch (Exception e) {
e.printStackTrace();
}
}
// 还真是这样,自定义代理类
public static class MyOnClickListener implements View.OnClickListener {
private final Context context;
View.OnClickListener onClickListener;
public MyOnClickListener(View.OnClickListener onClickListener, Context context){
this.onClickListener = onClickListener;
this.context = context;
}
@Override
public void onClick(View v) {
Log.d("SetOnClickListener", "点击事件被hook到了");
Toast.makeText(context,"哈哈哈,事件被我吃了!",Toast.LENGTH_SHORT).show();
// if (onClickListener != null) {
// onClickListener.onClick(v);
// }
}
}
}
3.其实上面代码中的每一步都写的很清楚了;可以发现就是使用反射来替换里面的对象;
4.分析过程:
首先查看点击事件源码:
这里可以发现,把我们new的onclicklisterner对象赋值给了getListenerInfo().mOnClickListener;
我们来看看getListenerInfo得到了什么:
看到ListenerInfo是一个对象,点进去发现是View类中的一个内部类(所以上面代码中使用发射时填入了内部类的类名);而mOnClickListener就是此内部类的一个属性;所以上面代码中,我们用反射获取到了这个属性,并且得到了刚刚我们传进去的那个真实的clicklistener对象;
当我们拿到了这个真实的clicklistener对象后,就可以为所欲为了;
接下来给这个属性重新赋值;我们可以传个null;直接把点击事件屏蔽掉;也可以自己实现一个点击事件,把真实的点击事件传进去;但在执行点击事件的前后都可以加入我们自己的功能。这样就达到了屏蔽点击事件或修改点击事件或直接给我我们想做的事情。
5.参考:https://www.jianshu.com/p/74c12164ffca;非常感谢!