博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
我学安卓——运行时hook之onClickListener
阅读量:6635 次
发布时间:2019-06-25

本文共 3800 字,大约阅读时间需要 12 分钟。

  hot3.png

最近在做一个无埋点项目,一开始的方案是要做运行时hook,后来改成了编译期hook,但是我认为运行时的hook还是有技术场景的,所以分享一下。

关于hook

其实所谓的hook,并没有想的那么高深。hook本来是钩子的意思,以前在windows平台上做东西,经常需要通过某种手段去改变系统API的一个行为,把系统的某个方法或者某个属性指向他处,从而改变系统的工作流程,这是我最早接触的hook技术。然而java中,一般来说,不需要这么底层,只需要将原本的某个对象A,替换为我们的另一个对象B就可以,原本由A执行的逻辑,交给了B来执行,而对此常用的具体技术就是反射和代理。 我们使用hook技术,主要是关心hook点在哪,而这需要对源码有一定的了解。

获取view的OnClickListener

我们想要更改OnClickListener,那么首先我们要能获取到view的OnClickListener才行,很遗憾,view并没有提供getOnClickListener的方法,那么就只能看看源码了。 我们从setOnClickListen这个方法开始看,

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

可以看到是交给了getListenerInfo方法返回的对象,由此跟踪到View中有个ListenerInfo的属性,即mListenerInfo,它才是OnClickListener即mOnClickListener的真正持有者———这就是我们要hook的点了。

编写自己的OnClickListener

这部分没什么好说的,看代码就行:

/**     * clickListener的代理类     */    static class ClickListenerProxy implements View.OnClickListener{        private View.OnClickListener onClickListener;        public ClickListenerProxy(View.OnClickListener onClickListener) {            this.onClickListener = onClickListener;        }        @Override        public void onClick(View v) {            //执行自己的逻辑            Log.e("SSSS","proxy:"+fetchIDName(v.getContext(),v.getId()));            //执行原来的逻辑            if(onClickListener != null){                onClickListener.onClick(v);            }        }    }

对于方法fetchIDName的定义如下:

//id与id名称的对应private static final SparseArray
idNames = new SparseArray
();/***反射R类,获取对应id的id名称**/public static String fetchIDName(Context context, int idValue){ initIdNames(context.getPackageName()); String name = idNames.get(idValue); return name; } private static void initIdNames(String packageName){ if(idNames.size()>0){ return; } try { Class idClz = Class.forName(packageName + ".R$id"); Field[] fields = idClz.getDeclaredFields(); if (fields == null || fields.length == 0) { return ; } for (Field field : fields) { if (field == null) { continue; } field.setAccessible(true); try { idNames.put((Integer) field.get(null), field.getName()); }catch (Exception e){ e.printStackTrace(); } } }catch (Exception e){ e.printStackTrace(); } }

查找所有view,并替换原来绑定的OnClickListern

这一部分就是遍历ViewTree来查找view,并更改其中的ListenerInfo对象的OnClickListener属性。

反射获取hook点

/***获取View的Class,ListenerInfo的class及属性**/private static void initOnce() throws ClassNotFoundException, NoSuchFieldException {        if(viewClass == null) {            viewClass = Class.forName("android.view.View");        }        if (listenerInfoField == null) {            listenerInfoField = viewClass.getDeclaredField("mListenerInfo");            listenerInfoField.setAccessible(true);        }        if(listenerInfoClass == null) {            listenerInfoClass = Class.forName("android.view.View$ListenerInfo");        }        if(clickListenerField == null) {            clickListenerField = listenerInfoClass.getDeclaredField("mOnClickListener");            clickListenerField.setAccessible(true);        }    }

遍历viewTree,逐一hook

public  static void injectListerner(Activity activity) throws NoSuchFieldException, ClassNotFoundException {        initOnce();//获取hook点        View view = getContentView(activity);        if (view instanceof ViewGroup){            ViewGroup viewGroup = (ViewGroup)view;            for(int i = 0;i

至此编码完成,这种方式不仅可以对我们平时调用setOnClickListener方法的事件拦截,可以对xml中定义的OnClick事件拦截,只是因为遍历view树,并较多的使用了反射,所以导致性能上会略微差点。

转载于:https://my.oschina.net/tnjin/blog/1408160

你可能感兴趣的文章
iOS开发UI篇—核心动画简介
查看>>
Python黑帽编程2.7 异常处理
查看>>
Java程序员的日常—— POI与JDBC、Mockmvc与单元测试
查看>>
游戏开发技能树(转)
查看>>
eclipse 创建项目时出现appcompat_v7?
查看>>
如何扩展 Visual Studio 编辑器
查看>>
easyui_extension.js
查看>>
【温故而知新-Javascript】使用地理定位
查看>>
第六十八节,htnl全局属性和其他属性
查看>>
Eclipse窗口总是在最前的解决办法
查看>>
finecms如何控制调用子栏目的数量
查看>>
Redis常用命令解析——INFO, MONITOR, SLOWLOG
查看>>
es-02-elasticsearch安装及遇到的问题
查看>>
LogcatHelperDemo【应用log信息保存成本地文件】
查看>>
tmp32dll\sha1-586.asm(1432) : error A2070:invalid instruction operands 编译openssl出错
查看>>
导入 flash 时 failed to import activex 的解决
查看>>
Symfony学习--目录和入口
查看>>
老年人前列列腺炎
查看>>
Android -- 系统和自定义Notification
查看>>
百度编辑器UEditor ASP.NET示例Demo 分类: ASP.NET...
查看>>