复习的时候看到这样一个问题,这是插件化的一个知识点,实现一下加深印象
当调用startActivity()时会通过Instrumentation调用AMS去启动Activity,AMS经过一系列的处理后通知ApplicationThread创建Activity,ApplicationThread又用Handler(即mH)把消息转到主线程,即sendMessage(LAUNCH_ACTIVITY);之后Instrumentation通过反射创建Activity,调用该Activity的生命周期(上述流程可能在各个android版本有差异)。
通过上述流程可知,最后启动Activity时会发送Handler的LAUNCH_ACTIVITY消息(api 27)
public final class ActivityThread {
...
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
...
}
然后执行handleLaunchActivity()方法,把ActivityClientRecord作为参数传入
//ActivityThread.class
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
...
Activity a = performLaunchActivity(r, customIntent);
...
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
...
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//Instrumentation创建activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
...
}
//Instantiation.class
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
经过一些列调用,最后执行到mInstrumentation.newActivity(),Instantiation反射创建Activity,className即
ActivityClientRecord.intent。因此在反射创建之前把这个intent替换成真正要启动的Activity,就能实现启动未注册的Activity了。通过上面的分析可知LAUNCH_ACTIVITY消息会携带的msg.obj就是ActivityClientRecord。而Hanlder在调用handleMessage()之前会先执行CallBack里的方法
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
因此插件化的做法是hook Handler,通过反射的方式设置mH里的mCallBack值达到启动未注册的Activity的目的。
Handler的mCallBack是不能通过外部方法设置的,只能通过反射,因此要先拿到ActivityThread里的mH对象;ActivityThread无法直接获取,同样需要用反射。注意到ActivityThread中有个静态方法可以获取到实例。
//ActivityThread.class
public static ActivityThread currentActivityThread() {
return sCurrentActivityThread;
}
因此实现如下:
//关键方法
override fun hook() {
//拿到ActivityThread
val activityThreadClass = Class.forName("android.app.ActivityThread")
val currentActivityThread: Method = activityThreadClass.getDeclaredMethod("currentActivityThread")
val activityThreadObj: Any? = currentActivityThread.invoke(null)
//拿到mH
val mHField: Field = activityThreadClass.getDeclaredField("mH")
mHField.isAccessible = true
val mH: Handler = mHField[activityThreadObj] as Handler
//拿到Handle里的mCallBack
val callBackField: Field = Handler::class.java.getDeclaredField("mCallback")
callBackField.isAccessible = true
//赋新值
callBackField.set(mH, object : Handler.Callback {
override
fun handleMessage(msg: Message): Boolean {
//LAUNCH_ACTIVITY = 100
if (msg.what == 100) {
Log.e(TAG, "handleMessage: LAUNCH_ACTIVITY")
val record: Any = msg.obj
try {
Log.d(TAG, "hookActivityClientRecord: " + hookActivityClientRecord(record))
} catch (e: Exception) {
e.printStackTrace()
}
}
//继续执行handleMessage
return false
}
})
}
/**
* 替换掉ActivityClientRecord中的Intent
*
* @param record
* @return
* @throws Exception
*/
@SuppressLint("PrivateApi")
private fun hookActivityClientRecord(record: Any): Boolean {
//内部类反射需要用xxx.xxx$xxx
val recordClass = Class.forName("android.app.ActivityThread\$ActivityClientRecord")
val intentField = recordClass.getDeclaredField("intent")
intentField.isAccessible = true
//拿到intent
val intent = intentField[record] as Intent
//目标Activity,没有就返回
val targetIntent = intent.getParcelableExtra<Intent>(Hooker.extra) ?: return false
//修改intent
intentField[record] = targetIntent
return true
}
上面的代码实现了Intent的替换,但是由于AMS还有一系列校验,因此仍然需要使用一个已注册的Activity作为代理,把真正要打开的Activity Intent当做参数传递过来用做替换。
/**
* Description: hook Activity
* Author : pxq
* Date : 2020/5/14 9:17 PM
*/
class App : Application() {
override fun onCreate() {
super.onCreate()
Hooker.hook(Build.VERSION.SDK_INT)
}
}
/**
* Description: 代理Activity(已注册)
* Author : pxq
* Date : 2020/5/14 9:44 PM
*/
class ActivityStub : AppCompatActivity() {
val TAG = ActivityStub::class.java.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "ActivityStub")
}
}
/**
* Description: 未注册的Activity
* Author : pxq
* Date : 2020/5/14 9:45 PM
*/
class TargetActivity : AppCompatActivity() {
val TAG = TargetActivity::class.java.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.e(TAG, "TargetActivity 启动...")
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun open(view: View) {
val intent = Intent(this, ActivityStub::class.java).apply {
//要启动的Activity
val bundle = Bundle()
bundle.putParcelable(Hooker.extra, Intent(this@MainActivity, TargetActivity::class.java))
putExtras(bundle)
}
startActivity(intent)
}
}
Android 6.0上测试效果:
GitHub传送门:如何打开未注册的Activity