Commands that ActivityThread can handle.

ActivityThread holds a Binder object which communicate with AMS, it is called ApplicationThread. it is responsible to call corresponding activity lifecycle callbacks from AMS, ActivityThread Handler can process 50 messages type. they are :

        public static final int LAUNCH_ACTIVITY         = 100;
        public static final int PAUSE_ACTIVITY          = 101;
        public static final int PAUSE_ACTIVITY_FINISHING= 102;
        public static final int STOP_ACTIVITY_SHOW      = 103;
        public static final int STOP_ACTIVITY_HIDE      = 104;
        public static final int SHOW_WINDOW             = 105;
        public static final int HIDE_WINDOW             = 106;
        public static final int RESUME_ACTIVITY         = 107;
        public static final int SEND_RESULT             = 108;
        public static final int DESTROY_ACTIVITY        = 109;
        public static final int BIND_APPLICATION        = 110;
        public static final int EXIT_APPLICATION        = 111;
        public static final int NEW_INTENT              = 112;
        public static final int RECEIVER                = 113;
        public static final int CREATE_SERVICE          = 114;
        public static final int SERVICE_ARGS            = 115;
        public static final int STOP_SERVICE            = 116;

        public static final int CONFIGURATION_CHANGED   = 118;
        public static final int CLEAN_UP_CONTEXT        = 119;

        ...
        public static final int RELAUNCH_ACTIVITY       = 126;
        ...
        public static final int CANCEL_VISIBLE_BEHIND = 147;
        public static final int BACKGROUND_VISIBLE_BEHIND_CHANGED = 148;
        public static final int ENTER_ANIMATION_COMPLETE = 149;

LAUNCH_ACTIVITY, CONFIGURATION_CHANGED, RELAUNCH_ACTIVITY to name a few important ones.

CONFIGURATION_CHANGED

When AMS receives configuraiton changes from system, it calls boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, boolean persistent, boolean initLocale) { int changes = 0;

    if (values != null) {
    ...
            for (int i=mLruProcesses.size()-1; i>=0; i--) {
                ProcessRecord app = mLruProcesses.get(i);
                try {
                    if (app.thread != null) {
                        app.thread.scheduleConfigurationChanged(configCopy);
                    }
                } catch (Exception e) {
                }
            }
    ...
    boolean kept = true;
    final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
    if (mainStack != null) {
    ...
        if (starting != null) {
            kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
            mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
        }
    }


    return kept;
}

This method is trying to do 2 things:

  1. change the current configuration, and
  2. make sure the given activity is running with the (now) current configuration. Returns true if the activity has been left running, or false if starting is being destroyed to match the new configuration.

scheduleConfigurationChanged will send a CONFIGURATION_CHANGED to its message queue, which then perform configuration changes for all componnents in current ActivityThread, this includes Application, Providers, Service, Activities.

    final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
        ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
        freeTextLayoutCachesIfNeeded(configDiff);

        if (callbacks != null) {
            final int N = callbacks.size();
            for (int i=0; i<N; i++) {
                performConfigurationChanged(callbacks.get(i), config);
            }
        }
    }

for all other components except Activity, performConfigurationChanged will trigger its onConfigurationChanged callback by default, but for Activity it depends on the following statement to be true(The configuraion changed is not what Activity desinged to handle):

                if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
                    shouldChangeConfig = true;
                }

Each Component including Activity will receive CONFIGURATION_CHANGED event, depends on the component's configuration, it will call the onConfigurationChanged callback, or choose to not call.

ensureActivityConfigurationLocked on the other hand, make sure the activity is destroyed/recreated as needed. still this method checks getRealConfigChanged() to check if RELAUNCH_ACTIVITY should be sent.

    /**
     * Make sure the given activity matches the current configuration.  Returns
     * false if the activity had to be destroyed.  Returns true if the
     * configuration is the same, or the activity will remain running as-is
     * for whatever reason.  Ensures the HistoryRecord is updated with the
     * correct configuration and all other bookkeeping is handled.
     */
    final boolean ensureActivityConfigurationLocked(ActivityRecord r,
            int globalChanges) {
        ...
        if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) {

                relaunchActivityLocked(r, r.configChangeFlags, true);
                r.configChangeFlags = 0;
        ...
        }
    }
RELAUNCH_ACTIVITY

When AMS need to relaunch an activity, it will send RELAUNCH_ACTIVITY to ActivityThread and perform relaunching activity.

the token in activity record representing the Acitivity, this token will be send back to AcitivityThread and used to find the Activity need to be destroyed. this token shows up as param in the handleRelaunchActity().

    private void handleRelaunchActivity(ActivityClientRecord tmp) {
    ...
    if (!r.paused) {
            performPauseActivity(r.token, false, r.isPreHoneycomb());
        }
        if (r.state == null && !r.stopped && !r.isPreHoneycomb()) {
            callCallActivityOnSaveInstanceState(r);
        }

        handleDestroyActivity(r.token, false, configChanges, true);
        ...
        handleLaunchActivity(r, currentIntent);
    }

handleRelaunchAcitivity does 2 things,

  • destroy activity, the last param is true, which tell the ActivityThread to save the non-configuration-instance(Configuration and NonConfigurationInstance)
  • launch the destroyed activity with saved intent. non-configuration changes such as fragment and loaders are passed long to new activity instance during parsing the intent.
    private void handleDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance) {
        ActivityClientRecord r = performDestroyActivity(token, finishing,
                configChanges, getNonConfigInstance);
...
            if (r.mPendingRemoveWindow == null) {
                //THE PLACE FRAGMENT/DIALOG's mDECOR is destroyed. 
                WindowManagerGlobal.getInstance().closeAll(token,
                        r.activity.getClass().getName(), "Activity");
...
    }

    private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance) {
        ActivityClientRecord r = mActivities.get(token);
        ...
            if (getNonConfigInstance) {
                try {
                    r.lastNonConfigurationInstances
                            = r.activity.retainNonConfigurationInstances();
                } catch (Exception e) {
                    if (!mInstrumentation.onException(r.activity, e)) {
                        throw new RuntimeException(
                                "Unable to retain activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
                    }
                }
            }
     ...
    }

NonConfiguration of Activity instance is then saved into ActivityRecord and recover from it later.

results matching ""

    No results matching ""