Hook 实例以及注意事项

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);
            }
        });

前半部分讲解代码是我纯手写,流程和源代码一样,命名会有些许不同,但不会影响阅读。
有些代码借鉴自其它博客,暂时找不到地址了,如侵权,请告知。

猜你喜欢

转载自blog.csdn.net/chenxiaoping1993/article/details/78979569
今日推荐