hook 又名钩子,有啥用呢?
悄无声息的去往别人的代码中添加一些自己的逻辑。
怎么做呢?不再介绍什么 代理模式,反射语法啥的了。
说说初学者遇到的一些细节吧。
例子,防止按钮重复点击:
我们先看看源码:
button.setOnClickListener(newView.OnClickListener() {
很明显,我们可以从这儿看出,执行点击后的逻辑的是 View.OnClickListener();所以我们要找这个对象去哪儿了。
View 类
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
// 我们看到这儿,这个对象 l 被赋值给 getListenerInfo()
getListenerInfo().mOnClickListener = l;
}
继续看 getListenerInfo() 方法,发现是赋值给了 mListenerInfo 这个对象
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
static class ListenerInfo {
// 最后发现是 View 类中的一个静态内部类
public OnClickListener mOnClickListener;
我们最终希望不让执行 onClick() 方法,那我们把这个 mOnClickListener 对象替换掉不就可以了吗?大概就是这样一个思路。我们开始写代码:
代码思路:
1、我们要得到我们要拦截对象的 mOnClickListener
2、我们创建自己的 mOnClickListener
3、用我们自己的 mOnClickListener 来替换原控件的 mOnClickListener
要得到类或者方法,首先要获得这个类或者方法的字节码
注意:每个类的字节码,在内存中只有一份,每一份字节码就是一个Class的实例对象
要想得到 mOnClickListener ,如上图所示,我们需要这样几步
1> 得到 View 类中的实例 mListenerInfo
1.1 得到 View类的字节码
Class<?> viewClass = Class.forName("android.view.View");
1.2 得到 View 的内部类实例 mListenerInfo (得到方法,得到实例)
Method getListenerInfoMethod = viewClass .getDeclaredMethod("getListenerInfo");
// 判断该方法是否访问,如果不能,则强制访问
if(!getListenerInfoMethod.isAccessible()){
getListenerInfoMethod.setAccessible(true);
}
Object listenerInfoObj = getListenerInfoMethod.invok(view);// 这个 View 是我们传经来的 Button 对象
2> 得到 listenerInfo 这个内部类中的 mOnClickListener 对象
还是一样的流程
Class<?> listenerInfoClass = Class.forName("android.view.View$ListenerInfo");
Field onClickListenerField = listenerInfoClass.getDeclaredField("mOnClickListener");
if(!onClickListenerField .Accessible()){
onClickListenerField .setAccessible(true);
}
// 得到了 OnClickListener 实例
Object onClickListenerObj = onClickListenerField .get(listenerInfoObj );
View.OnClickListener mOnClickListenerObj = (View.OnClickListener)onClickListenerObj;
---我是一只小毛驴(书签)---
3>创建要替换的 OnClickListener 对象
3.1 创建一个内部类
public class MyOnClickListenerProxy implements View.onClickListener{
private View.OnClickListener mOnClickListener;
public MyOnClickListenerProxy(View.OnClickListener listener){
this.mOnClickListener = listener;
}
@Override
public void onClick(View view){
Log.d("haha","ha ha , ni de an niu yi jin bei wo jie chi le ");
}
}
3.2 进行替换
---我是一只小毛驴(接上文)---
View.OnClickListener mListenerProxy = new MyOnClickListenerProxy(mOnClickListenerObj );
mOnClickListenerObj.set(mOnClickListenerObj ,mListenerProxy );
bingo!!,搞定
看完整源码:
class ListenerHookUtils{
public static void hookUtils(View view){
// 目标: 我们要替换 View 的 listenerInfo 的 onClickListener 字段
try {
//获取我们要替换的对象
//获取到 View
Class<?> myView = Class.forName("android.view.View");
// 获取到 setOnClickListener 方法
Method getListenerInfoMethod = myView.getDeclaredMethod("getListenerInfo");
if(!getListenerInfoMethod.isAccessible()){
getListenerInfoMethod.setAccessible(true);
}
// 获取到我们要替换的对象
Object listenerInfoObj = getListenerInfoMethod.invoke(view);
Class<?> listenerInfoClass = Class.forName("android.view.View$ListenerInfo");
Field onClickListenerFiled = listenerInfoClass.getDeclaredField("mOnClickListener");
if(!onClickListenerFiled.isAccessible()){
onClickListenerFiled.setAccessible(true);
}
View.OnClickListener onClickListener = (View.OnClickListener) onClickListenerFiled.get(listenerInfoObj);
//创建一个 onClickListener 对象
View.OnClickListener myClickListener = new MyOnclickListenerProxy(onClickListener);
//listenerInfoObj.
onClickListenerFiled.set(listenerInfoObj,myClickListener);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
static class MyOnclickListenerProxy implements View.OnClickListener{
private View.OnClickListener object;
private int MIN_CLICK_DELAY_TIME = 1000;
private long lastClickTime = 0;
private MyOnclickListenerProxy(View.OnClickListener object) {
this.object = object;
}
@Override
public void onClick(View v) {
long currentTime = Calendar.getInstance().getTimeInMillis();
if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {
lastClickTime = currentTime;
Log.("haha", "haha ni de an niu bei wo jie chi le ");
// 下边这句话的意思是,执行完我们的逻辑之后,继续执行原有逻辑
if (object != null) object.onClick(v);
}
}
}
}
调用源码:
bt_click=findViewById(R.id.bt_click);
bt_click.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this,Main2Activity.class));
}
});
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
ListenerHookUtils.hookUtils(bt_click);
}
});
前半部分讲解代码是我纯手写,流程和源代码一样,命名会有些许不同,但不会影响阅读。
有些代码借鉴自其它博客,暂时找不到地址了,如侵权,请告知。