插件化中需要使用到hook技术,这里先了解下hook技术,以方便继续看代码,既然是代理,那就先来了解下代理设计模式吧。
1 代理
代理,或者称为 Proxy ,简单理解就是事情我不用去做,由其他人来替我完成。
1.1 无代理
通过例子来学习下,首先一个接口Human,有两个方法,一个是吃饭,一个上厕所
再看下其实现类:
测试下
最后运行结果:
1.2 静态代理
我们在吃法和上厕所前都要洗手,那么实现一个静态代理类在Human实现类基础上加上洗手。
测试例子:
运行结果:
1.3 JDK动态代理
在吃饭和上厕所前都要洗手,那不是每个都要写一遍,如果还有其他的比如玩泥巴后洗手等等,那得写多少遍,这个时候可以用动态代理。来实现下:
测试例子:
运行结果:
通过JDK动态代理,我们实现了需求。
2 Hook技术
- Hook英文翻译为“钩子”,而钩子就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事件,简单来说就是当我们开发过程中某些API不能满足我们的要求时,我们就得去修改某些api,使之能满足我们的要求。
- Hook的基本的方法就是通过hook“接触”到需要修改的api函数入口点,改变它的地址指向新的自定义的函数。在Android开发中,我们同样能利用Hook的原理让系统某些方法运行时调用的是我们定义的方法,从而满足我们的要求。
- 一般的Hook 的选择点是静态变量和单例,因为一旦创建对象,它们不容易变化,非常容易定位。
- Hook 过程是寻找 Hook 点,原则是静态变量或者单例对象,尽量 Hook public 的对象和方法。选择合适的代理方式,如果是接口可以用动态代理,也可以自己写静态代理替换原始对象。
3 Hook掉startActivity
有了以上基础,接着我们可以开始Hook掉startActivity方法,这里参考了VitrualApk的源码。
3.1 startActivity源码
Context.startActivity只是接口,其实现类是ContextImpl,具体代码如下:
不难发现最终是通过调用ActivityThread类中的mInstrumentation成员的execStartActivity方法。
3.2 获取ActivityThread对象
可以看到sCurrentActivityThread是一个静态变量,主线程又只有一个,只要获取了这个ActivityThread,修改其mInstrumentation对象为代理的对象,那么就可以hook了。
接着我们通过反射来获取:
3.3 获取mInstrumentation对象
获取到sCurrentActivityThread后就可以通过getInstrumentation方法获取mInstrumentation,同样通过反射:
3.4 创建代理的VAInstrumentation
接着我们创建自己的VAInstrumentation来替换mInstrumentation对象,由于mInstrumentation是类不是接口,不能用动态代理,这里写一个静态的代理类来覆盖原方法。
在VAInstrumentation中复写了execStartActivity,加了一行log信息,然后继续调用系统真实的realExecStartActivity方法。
3.5 设置instrumentation为我们代理的对象
最后我们把创建的代理替换下:
然后我们再调用startActivity启动另一个Activity看下log信息:
调用了我们自己的代理类了,已经被hook了,之后就可以做很多其他事情了,比如Activity必须要在AndroidManifest中注册,而插件化的时候是不可能预先知道插件中的Activity的,可以通过hook,占坑等来实现,这个后续再来实现下,基本上插件化的hook机制相信都能明白了。
github源码:https://github.com/eastmoon1117/StudyTestCase/tree/master/VirtualAppDemo