假如,我们现在要在每次启动一个activity之前都要打印一条信息。
最笨的方式就是用一个BaseActivity集成Activity,但是,如果我们这个项目已经进行了N多年,想象一下,这得修改多少源码,且这破坏了我们的设计原则,我们尽量扩展而不是修改代码。那些已有的代码都是经历时间考验的,修改之后可靠性会下降。
当我们启动一个Activity的时候,肯定都会调用startActivity**之类的代码
1 | startActivity(intent); |
我们来看下:
1 | /** |
它调用了activity的 startActivity(Intent, Bundle)
再跟踪:
1 | @Override |
又调用了startActivityForResult(Intent intent, int requestCode)
1 | public void startActivityForResult(Intent intent, int requestCode) { |
又调用了startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options)
1 | /** |
可以看到最后它是调用了Instrumentation类的execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options, UserHandle user)方法
1 | public ActivityResult execStartActivity( |
继续跟踪是不可能了,我们无法找到一个稳定的hook点
不如就从Instrumentation这个类下手,看下它是从哪里初始化的:
1 | final void attach(Context context, ActivityThread aThread, |
attach ?这是从哪里来的,看过android源码的人都知道,attach是由ActivityThread这个类调用的,显然我们可以把下手目标指向ActivityThread了。搜索一下,可以看到:
1 |
|
是的在这里:
1 | activity.attach(appContext, this, getInstrumentation(), r.token, |
切进去看下:
1 | public Instrumentation getInstrumentation(){ |
可以看到mInstrumentation是ActivityThread的一个成员变量,不过,既然找到ActvityThread了,所有的问题都解决了,为什么,因为每个应用在运行时都只有一个ActivityThread,它用来代表主线程,它是单例的。
获取ActivityThread也很简单
1 | public static ActivityThread curretActivityThread() { |
我们定义一个Instrumentation的代理,它会在原来执行execStartActivity之前打印一段话。
1 | import android.app.Activity; |
现在万事俱备 开始Hook!
我们尽量最早的hook,所以我们打算在application里面。
自定义一个application:
1 | public class HookApplication extends Application { |