源码的执行是按照一定流程思路进行的,hook就是在源码的执行流程之间插入一步操作,起到拦截,替换的作用;被改变的对象称为hook点,一般将不易发生变化的类作为hook点;
常见的hook点有:
- 静态变量
- 单例
代理模式:
学习hook必须了解代理模式,可以参考我这篇文章:反射和动态代理
Hook startActivity
首先需要知道startactivity的流程:Android进阶解密①——activity的启动过程
我们知道startActivity会通过mInstrumentation这个类,我们可以将这个类作为hook点;
创建一个自定义的Instrumentation:
public class InstrumentationProxy extends Instrumentation {
Instrumentation instrumentation;
public InstrumentationProxy(Instrumentation instrumentation) {
this.instrumentation = instrumentation;
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String target,
Intent intent, int requestCode, Bundle options) {
// TODO: hook 操作
try {
Method methodExecStartActivity = Instrumentation
.class.getDeclaredMethod("execStartActivity",
Context.class,IBinder.class,
IBinder.class,Activity.class,
Intent.class,int.class,Bundle.class);
return (ActivityResult) methodExecStartActivity.invoke(instrumentation,who,contextThread,token,target,intent,requestCode,options);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
自定义一个Instrumentation,在activity的工作过程中通过反射替换原来的Instrumentation,将原来的Instrumentation传到代理类里面,通过method invoke保证原来的功能不变,然后可以添加自己的自定义操作;
替换原来流程的Instrumentation:
private void replaceInstrumentation(Activity activity) throws NoSuchFieldException, IllegalAccessException {
Field field = activity.getClass().getDeclaredField("mInstrumentation");
field.setAccessible(true);
Instrumentation instrumentation = (Instrumentation) field.get(activity);
Instrumentation instrumentationProxy = new InstrumentationProxy(instrumentation);
field.set(activity,instrumentationProxy);
}
首先拿到activity原来的Instrumentation对象,通过原来的Instrumentation构建出一个InstrumentationProxy对象,将Proxy设置给activity,然后只要在startActivity()之前调用这个方法替换就可以了;