目的
希望程序员每次调用startActivity()的时候,可以多打印一条日志。
实现原理
startActivity() 执行过程中,会调用到ActivityThread的成员对象mInstrumentation的 execStartActivity()函数,于是我们可以自定义一个EvilInstrumentation(extends Instrumentation )类,重写execStartActivity()函数,追加实现自己的逻辑,然后通过反射把ActivityThread的成员对象mInstrumentation替换为我们自己写的EvilInstrumentation的对象,达到偷梁换柱的目的。
代码实现
App.java
package com.qunar.yuzhiyun.hook;
import android.app.Application;
import android.app.Instrumentation;
import android.content.Context;
import java.lang.reflect.Field;
/**
* Created by yuzhiyun on 17/8/11.
*/
public class App extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// 先获取到当前的ActivityThread对象
Class activityThreadClass = null;
try {
activityThreadClass = Class.forName("android.app.ActivityThread");
Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
//传给get和set方法的参数必须是拥有该字段的实例。如果字段是静态字段(public static …),则传null作为get和set方法的参数
Object currentActivityThread = currentActivityThreadField.get(null);
// 拿到原始的 mInstrumentation字段
Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);
// 创建代理对象
Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);
// 偷梁换柱
mInstrumentationField.set(currentActivityThread, evilInstrumentation);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
EvilInstrumentation.java
package com.qunar.yuzhiyun.hook;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import java.lang.reflect.Method;
/**
* Created by yuzhiyun on 17/8/11.
*/
public class EvilInstrumentation extends Instrumentation {
private static final String TAG = "EvilInstrumentation";
// ActivityThread中原始的对象, 保存起来
Instrumentation mBase;
public EvilInstrumentation(Instrumentation base) {
mBase = base;
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
// Hook之前, XXX到此一游!
Log.d(TAG, "\n执行了startActivity, 参数如下: \n" + "who = [" + who + "], " +
"\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +
"\ntarget = [" + target + "], \nintent = [" + intent +
"], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]");
// 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了.
// 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
try {
Method execStartActivity = Instrumentation.class.getDeclaredMethod(
"execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class,
Intent.class, int.class, Bundle.class);
execStartActivity.setAccessible(true);
return (ActivityResult) execStartActivity.invoke(mBase, who,
contextThread, token, target, intent, requestCode, options);
} catch (Exception e) {
// 某该死的rom修改了 需要手动适配
throw new RuntimeException("do not support!!! pls adapt it");
}
}
}