Skip to content

Android dynamic register activities

galen edited this page Jan 4, 2016 · 3 revisions

Android activities are monitored by Instrumentation.

Android activities受Instrumentation监控。

Each activity was started by Activity's startActivityForResult. (1)

ActivitystartActivityForResult方法启动,通过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)

ActivityThreadperformLaunchActivity方法中通过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);
    }
}