详细讲解下Hook技术,以Hook点击事件来示范

Hook技术:  Hook就是有一段程序逻辑一直走下去,我们可以捕获到其中间的一些逻辑,加于处理然后再让他接着执行下去;

比如Android里面的setOnclickListener这个方法. 正常我们是这样操作的

        TextView textView = findViewById(R.id.act_invoke_tv);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(InvokeOnClickActivity.this, ((TextView)v).getText(), Toast.LENGTH_SHORT).show();
            }
        });

怎么在不改变这段代码的情况下,把里面的的Toast内容给替换掉

分析思路: 1肯定要用到动态代码,动态代码用到的是Proxy这个类

        Object proxyInstance = Proxy.newProxyInstance(getClassLoader(), new Class[]{View.OnClickListener.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Toast.makeText(InvokeOnClickActivity.this, "已经hokd到方法了", Toast.LENGTH_SHORT).show();
                return method.invoke(mOnClickListener, null);
            }
        });

等我们通过反射获取到textView设置的mClickListener的时候就将其替换

首先我们在setOnclickListener的时候传的是一个View.OnClickListener过去

   public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

这方法里面将我们传入的OnclickListener附值给了getListenerInfo()获取的类的一个成员变量,而getListenerInfo()这个方法获取的是ListenerInfo这个类,也就是说,在这里操作的是将我们设置的OnClickListener传给了ListenerInfo,是他里面的一个成员变量,

所以我们第一步就是获取到View里面的getListenerInfo这个方法

Class.forName这个方法可以获取到一个Class类,然后通过这个Class类获取到getListenerInfo这个方法,然后再执行这个方法代码如下

Class viewClass = Class.forName("android.view.View");
Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
getListenerInfo.setAccessible(true);
Object listenerInfo = getListenerInfoMethod.invoke(textView)

listenerInfo则就是通过VIew里的getListenerInfo获取到的对象  因为OnClickListener附给了ListenerInfo里的一个成员变量,这里我们继续通过反射获取到它的成员变量mOnClickListener

Object listenerInfoClass = Class.forName(android.view.View$ListenerInfo);
//获取到名字为mOnClickListener的成员变量
Field onClickListenerField = listenerInfoClass.getDeclaredField("mOnClickListener");
//获取到原来设置的OnClickListener,其实可以不用这个,只是为了不影响正常逻辑,因为我们代理完了要重新
//执行原来的方法
Object mOnClickListener = onClickListenerField.get(listenerInfo);

最后一步就是替换操作了

onClickListenerField.set(listenerInfo,proxyInstance);这样就远的成自己的了,当我们点击Teview的点击事件的时候就会先走到我们代码的proxyInstance里面的invoke方法,

你可能感兴趣的:(Android,Retrofit,android,java)