-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Android dynamic register activities
Android activities are monitored by Instrumentation.
Android activities受Instrumentation监控。
Each activity was started by Activity's startActivityForResult
. (1)
由Activity的
startActivityForResult
方法启动,通过instrumentation的execStartActivity
方法激活生命周期。
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity( // Override entry 1
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
...
}
}
And instantiated by ActivityThread's performLaunchActivity
. (2)
在ActivityThread的
performLaunchActivity
方法中通过instrumentation的newActivity
方法实例化。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity( // Override entry 2
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);
}
}
...
}
To dynamic register activities, we register a stub activity in host's AndroidManifest.xml
to cheat (1) to manager the activity life circle for us. And then cheat (2) to create the plugin activity instance.
动态注册Activity,首先在宿主manifest中注册一个命名特殊的占坑activity来欺骗(1)以获得生命周期,再欺骗(2)来获得插件activity实例。
So all the things to do is wrapper a instrumentation and replace the host one.
要做的就是封装一个instrumentation,替换掉宿主的。
Fake code:
<!-- Stub Activities -->
<activity android:name=".A.0" android:launchMode="standard"/>
ActivityThread thread = currentActivityThread();
Instrumentation base = thread.@mInstrumentation;
Instrumentation wrapper = new InstrumentationWrapper(base);
thread.@mInstrumentation = wrapper;
class InstrumentationWrapper extends Instrumentation {
public ActivityResult execStartActivity(..., Intent intent, ...) {
fakeToStub(intent);
base.execStartActivity(args);
}
@Override
public Activity newActivity(ClassLoader cl, String className, Intent intent) {
className = restoreToReal(intent, className);
return base.newActivity(cl, className, intent);
}
}