假如,我们现在要在每次启动一个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 {  |