详细讲解下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中bindService基本使用方法概述
- Unity 编辑器环境下不能正确加载Android Assetbundle 中的 Shade
- Android webView 获取、设置 cookie的方法
- Android常用控件的使用方法
- MPAndroidChart的一些问题解决方法
- 解决IE apk变成zip:Android 手机应用程序文件下载服务器 配置解决