Android插件化系列第(三)篇---Hook技术之View点击劫持
来源:互联网 发布:电吉他效果器软件app 编辑:程序博客网 时间:2024/06/14 00:39
昨天有好几个小伙伴简信问我,View.onClick怎么hook?回想前几个月前,公司的项目在百度手机助手上线,在快速点击的时候会跳转两次Activity或者两个Dialog等等,为了能够顺利通过百度的测试,老大叫我将所有onClick全部要优化处理,避免用户快速多次点击,于是乎,我写了下面的代码
public abstract class NoDoubleClickListener implements View.OnClickListener { private int MIN_CLICK_DELAY_TIME = 1000; private long lastClickTime = 0; public abstract void onNoDoubleClick(View v); @Override public void onClick(View v) { long currentTime = Calendar.getInstance().getTimeInMillis(); if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) { lastClickTime = currentTime; onNoDoubleClick(v); } }}
然后我打算这样来弄
btn.setOnClickListener(new NoDoubleClickListener() { @Override public void onNoDoubleClick(View v) { //something } });
可是面临一个问题,有这么多,改到何年马月啊?
OK,这个是我以前碰到的一个好蛋疼的问题,再比如,在不侵入业务代码的情况下监听所有的点击事件并记录所有的点击数,用于统计热点页面和其他一些分析工作,你怎么办呢?现在介绍一个如何Hook掉View的onClick方法,相对与上一篇,这个很简单了。
1、第一步寻找Hook点:
去看setOnClickListener里面做了什么?
btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } });
/** * Register a callback to be invoked when this view is clicked. If this view is not * clickable, it becomes clickable. * * @param l The callback that will run * * @see #setClickable(boolean) */ public void setOnClickListener(@Nullable OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; }
ListenerInfo getListenerInfo() { if (mListenerInfo != null) { return mListenerInfo; } mListenerInfo = new ListenerInfo(); return mListenerInfo; }
看完了上面,就能猜到我们设置的Listener最终是被赋值给ListenerInfo的mOnClickListener成员了,ListenerInfo的实例可以说是信息的载体,那么很简单,只要把mOnClickListener,在ListenerInfo中还有mOnLongClickListener,mOnFocusChangeListener两个成员,分别对应了长按事件与焦点变化事件,所以处理长按事件与焦点变化事件与此类似。
public class HookViewClickUtil { public static HookViewClickUtil getInstance() { return UtilHolder.mHookViewClickUtil; } private static class UtilHolder { private static HookViewClickUtil mHookViewClickUtil = new HookViewClickUtil(); } public static void hookView(View view) { try { Class viewClazz = Class.forName("android.view.View"); //事件监听器都是这个实例保存的 Method listenerInfoMethod = viewClazz.getDeclaredMethod("getListenerInfo"); if (!listenerInfoMethod.isAccessible()) { listenerInfoMethod.setAccessible(true); } Object listenerInfoObj = listenerInfoMethod.invoke(view); Class listenerInfoClazz = Class.forName("android.view.View$ListenerInfo"); Field onClickListenerField = listenerInfoClazz.getDeclaredField("mOnClickListener"); if (!onClickListenerField.isAccessible()) { onClickListenerField.setAccessible(true); } View.OnClickListener mOnClickListener = (View.OnClickListener) onClickListenerField.get(listenerInfoObj); //自定义代理事件监听器 View.OnClickListener onClickListenerProxy = new OnClickListenerProxy(mOnClickListener); //更换 onClickListenerField.set(listenerInfoObj, onClickListenerProxy); } catch (Exception e) { e.printStackTrace(); } } //自定义的代理事件监听器 private static class OnClickListenerProxy implements View.OnClickListener { private View.OnClickListener object; private int MIN_CLICK_DELAY_TIME = 1000; private long lastClickTime = 0; private OnClickListenerProxy(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.e("OnClickListenerProxy", "OnClickListenerProxy"); if (object != null) object.onClick(v); } } }}
使用起来也是非常简单,首先在MainActivity的View渲染完毕的时候进行注入,即在 getWindow().getDecorView().post()中。
public class MainActivity extends Activity { private Button btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final View btn = findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("MainActivity","Button 被点击了"); } }); getWindow().getDecorView().post(new Runnable() { @Override public void run() { HookViewClickUtil.hookView(btn); } }); }}
执行结果:
OK,到此完成了,至于怎么获取页面的所有View,调用 HookViewClickUtil.hookView(view),就不多说了。
Please accept mybest wishes for your happiness and success !
- Android插件化系列第(三)篇---Hook技术之View点击劫持
- Hook技术之View点击劫持
- 论读Android源码的重要性——Hook技术之View点击劫持
- Android插件化系列第(一)篇---Hook技术之Activity的启动过程的拦截
- hook view点击事件劫持
- Android插件化系列第(二)篇---动态加载技术之应用换肤
- android的hook技术之hook所有view的监听器
- Android Hook View技术实践
- 点击劫持(Clickjacking)漏洞技术内幕
- Android 插件化原理解析(3):Hook 机制之 Binder Hook
- 深入Android源码系列(二) HOOK技术大作战
- Web安全之点击劫持(ClickJacking)
- Web安全之点击劫持(ClickJacking)
- android插件化(Binder Hook)
- android插件化(AMS Hook)
- Android插件化系列第(五)篇---Activity的插件化方案(代理模式)
- Android插件化系列第(四)篇---插件加载机制两种方案
- Android学习第12天-----HOOK技术
- 如何利用TLS/SSL与防火墙规则保护CoreOS集群
- PHP检查文件存不存在,不存在自动创建,读取文件内容
- Android中Home键的监听和拦截
- javamail
- MySQL闪回原理与实战
- Android插件化系列第(三)篇---Hook技术之View点击劫持
- MOS管工作原理
- 查看Oracle 存储过程编译有错误信息
- css网格布局
- Vue.js学习之vue-router vuex axios webpack
- php敏感词处理
- Android资源引用中“?”和“@”区别
- POJ3006_Dirichlet's Theorem on Arithmetic Progressions_筛法求素数表
- poj 3254 Corn Fields