Android ComponentCallbacks的调用逻辑

来源:互联网 发布:linux echo 编辑:程序博客网 时间:2024/06/07 02:31

ComponentCallbacks接口的定义:

public interface ComponentCallbacks {
    /**
     * Called by the system when the device configuration changes while your
     * component is running.  Note that, unlike activities, other components
     * are never restarted when a configuration changes: they must always deal
     * with the results of the change, such as by re-retrieving resources.
     *
     * <p>At the time that this function has been called, your Resources
     * object will have been updated to return resource values matching the
     * new configuration.
     *
     * <p>For more information, read <a href="{@docRoot}guide/topics/resources/runtime-changes.html"
     * >Handling Runtime Changes</a>.
     *
     * @param newConfig The new device configuration.
     */
    void onConfigurationChanged(Configuration newConfig);


    /**
     * This is called when the overall system is running low on memory, and
     * actively running processes should trim their memory usage.  While
     * the exact point at which this will be called is not defined, generally
     * it will happen when all background process have been killed.
     * That is, before reaching the point of killing processes hosting
     * service and foreground UI that we would like to avoid killing.
     *
     * <p>You should implement this method to release
     * any caches or other unnecessary resources you may be holding on to.
     * The system will perform a garbage collection for you after returning from this method.
     * <p>Preferably, you should implement {@link ComponentCallbacks2#onTrimMemory} from
     * {@link ComponentCallbacks2} to incrementally unload your resources based on various
     * levels of memory demands.  That API is available for API level 14 and higher, so you should
     * only use this {@link #onLowMemory} method as a fallback for older versions, which can be
     * treated the same as {@link ComponentCallbacks2#onTrimMemory} with the {@link
     * ComponentCallbacks2#TRIM_MEMORY_COMPLETE} level.</p>
     */
    void onLowMemory();
}


public interface ComponentCallbacks2 extendsComponentCallbacks {

    /**
     * Called when the operating system has determined that it is a good
     * time for a process to trim unneeded memory from its process.  This will
     * happen for example when it goes in the background and there is not enough
     * memory to keep as many background processes running as desired.  You
     * should never compare to exact values of the level, since new intermediate
     * values may be added -- you will typically want to compare if the value
     * is greater or equal to a level you are interested in.
     *
     * <p>To retrieve the processes current trim level at any point, you can
     * use {@link android.app.ActivityManager#getMyMemoryState
     * ActivityManager.getMyMemoryState(RunningAppProcessInfo)}.
     *
     * @param level The context of the trim, giving a hint of the amount of
     * trimming the application may like to perform.  May be
     * {@link #TRIM_MEMORY_COMPLETE}, {@link #TRIM_MEMORY_MODERATE},
     * {@link #TRIM_MEMORY_BACKGROUND}, {@link #TRIM_MEMORY_UI_HIDDEN},
     * {@link #TRIM_MEMORY_RUNNING_CRITICAL}, {@link #TRIM_MEMORY_RUNNING_LOW},
     * or {@link #TRIM_MEMORY_RUNNING_MODERATE}.
     */
    void onTrimMemory(int level);
}


onConfigurationChanged回调的逻辑在 AMS.updateConfiguration中实现。代码如下:

    public voidupdateConfiguration(Configuration values) {

            final long origId = Binder.clearCallingIdentity();
            if (values != null) {
                Settings.System.clearConfiguration(values);
            }
            updateConfigurationLocked(values, null, false, false);
            Binder.restoreCallingIdentity(origId);
        }
    }


    boolean updateConfigurationLocked(Configurationvalues,
            ActivityRecord starting, boolean persistent, boolean initLocale) {
        int changes = 0;

        if (values != null) {
            Configuration newConfig = new Configuration(mConfiguration);
            changes = newConfig.updateFrom(values);
            if (changes != 0) {

                final Configuration configCopy = new Configuration(mConfiguration);
                
                // TODO: If our config changes, should we auto dismiss any currently
                // showing dialogs?
                mShowDialogs = shouldShowDialogs(newConfig);

                // Make sure all resources in our process are updated
                // right now, so that anyone who is going to retrieve
                // resource values after we return will be sure to get
                // the new ones.  This is especially important during
                // boot, where the first config change needs to guarantee
                // all resources have that config before following boot
                // code is executed.
                mSystemThread.applyConfigurationToResources(configCopy); // 更新system server的configuration


                if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
                    Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);// 更新settings system中的设置
                    msg.obj = new Configuration(configCopy);
                    mHandler.sendMessage(msg);
                }


                for (int i=mLruProcesses.size()-1; i>=0; i--) {
                    ProcessRecord app = mLruProcesses.get(i);
                    try {
                        if (app.thread != null) {
                            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
                                    + app.processName + " new config " + mConfiguration);
                            app.thread.scheduleConfigurationChanged(configCopy);    // 向每一个进程发送configuration changed的通知,进程会调用当前进程中,实现了ComponentCallbacks接口的成员(一般指Activity, Service, ContentProvider)
                        }
                    } catch (Exception e) {
                    }
                }
                Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                        | Intent.FLAG_RECEIVER_REPLACE_PENDING
                        | Intent.FLAG_RECEIVER_FOREGROUND);
                broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
                        null, AppOpsManager.OP_NONE, false, false, MY_PID,
                        Process.SYSTEM_UID, UserHandle.USER_ALL);    // 向系统中发送 configuration changed 广播
                if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
                    intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
                    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                    broadcastIntentLocked(null, null, intent,
                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                            false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);     // 向系统中发送 locale changed 广播
                }
            }
        }

        boolean kept = true;
        final ActivityStack mainStack = mStackSupervisor.getFocusedStack();  // 获得当前的stack
        // mainStack is null during startup.
        if (mainStack != null) {
            if (changes != 0 && starting == null) {
                // If the configuration changed, and the caller is not already
                // in the process of starting an activity, then find the top
                // activity to check if its configuration needs to change.
                starting = mainStack.topRunningActivityLocked(null);    // 获得正在running的activity
            }

            if (starting != null) {
                kept = mainStack.ensureActivityConfigurationLocked(starting, changes);  // 确保top activity获得了最新的configuration
                // And we need to make sure at this point that all other activities
                // are made visible with the correct configuration.
                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
            }
        }

        return kept;
    }


    final boolean ensureActivityConfigurationLocked(ActivityRecord r,
            int globalChanges) {

        // Okay we now are going to make this activity have the new config.
        // But then we need to figure out how it needs to deal with that.
        Configuration oldConfig = r.configuration;
        r.configuration = newConfig;      更新当前activity的配置

        // Determine what has changed.  May be nothing, if this is a config
        // that has come back from the app after going idle.  In that case
        // we just want to leave the official config object now in the
        // activity and do nothing else.
        final int changes = oldConfig.diff(newConfig);
        if (changes == 0 && !r.forceNewConfig) {          // 如果配置没有变化,则直接返回
            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                    "Configuration no differences in " + r);
            return true;
        }

        // If the activity isn't currently running, just leave the new
        // configuration and it will pick that up next time it starts.
        if (r.app == null || r.app.thread == null) {
            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                    "Configuration doesn't matter not running " + r);
            r.stopFreezingScreenLocked(false);
            r.forceNewConfig = false;
            return true;
        }

        // Figure out how to handle the changes between the configurations.
        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {      调式打印
            Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
                    + Integer.toHexString(changes) + ", handles=0x"
                    + Integer.toHexString(r.info.getRealConfigChanged())
                    + ", newConfig=" + newConfig);
        }
        if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) { // 配置有变化,并且activity does not declare to handle all the changes (Which changes activity want to handle is defined in android:configChanges attribute, e.g."android:configChanges="orientation|keyboardHidden|screenSize")
            // Aha, the activity isn't handling the change, so DIE DIE DIE.
            r.configChangeFlags |= changes;
            r.startFreezingScreenLocked(r.app, globalChanges);
            r.forceNewConfig = false;
            if (r.app == null || r.app.thread == null) {
                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                        "Config is destroying non-running " + r);
                destroyActivityLocked(r, true, "config");        如果进程还没有运行,则直接destroy
            } else if (r.state == ActivityState.PAUSING) {
                // A little annoying: we are waiting for this activity to
                // finish pausing.  Let's not do anything now, but just
                // flag that it needs to be restarted when done pausing.
                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                        "Config is skipping already pausing " + r);
                r.configDestroy = true;
                return true;
            } else if (r.state == ActivityState.RESUMED) {
                // Try to optimize this case: the configuration is changing
                // and we need to restart the top, resumed activity.
                // Instead of doing the normal handshaking, just say
                // "restart!".
                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                        "Config is relaunching resumed " + r);
                relaunchActivityLocked(r, r.configChangeFlags, true);  重启已经running的activity
                r.configChangeFlags = 0;
            } else {
                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                        "Config is relaunching non-resumed " + r);
                relaunchActivityLocked(r, r.configChangeFlags, false);   重启已经非running的activity
                r.configChangeFlags = 0;
            }

            // All done...  tell the caller we weren't able to keep this
            // activity around.
            return false;
        }

        // Default case: the activity can handle this new configuration, so
        // hand it over.  Note that we don't need to give it the new
        // configuration, since we always send configuration changes to all
        // process when they happen so it can just use whatever configuration
        // it last got.
        if (r.app != null && r.app.thread != null) {
            try {
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
                r.app.thread.scheduleActivityConfigurationChanged(r.appToken);  向指定的activity发送configuration change通知,最后会回调activity中的onConfigurationChanged。之前的scheduleConfigurationChanged也会回调onConfigurationChanged,但是在activity中有记录,不会重复调用
            } catch (RemoteException e) {
                // If process died, whatever.
            }
        }
        r.stopFreezingScreenLocked(false);

        return true;
    }


ActivityThread中调用onConfigurationChanged的逻辑

scheduleConfigurationChanged --> handleConfigurationChanged

    final voidhandleConfigurationChanged(Configuration config, CompatibilityInfo compat) {


        int configDiff = 0;


        synchronized (mResourcesManager) {
            mResourcesManager.applyConfigurationToResourcesLocked(config, compat);// 更新Resource,使用对应于configuration的资源

            if (mConfiguration == null) {
                mConfiguration = new Configuration();
            }
            if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
                return;
            }
            configDiff = mConfiguration.diff(config);
            mConfiguration.updateFrom(config);
            config = applyCompatConfiguration(mCurDefaultDisplayDpi);
        }

        ArrayList<ComponentCallbacks2> callbacks =collectComponentCallbacks(false, config);// 收集ComponentCallbacks的实现类

        if (callbacks != null) {
            final int N = callbacks.size();
            for (int i=0; i<N; i++) {
                performConfigurationChanged(callbacks.get(i), config);  // 调用每个实现类的onConfigurationChanged,并更新它的configuration
            }
        }
    }


scheduleActivityConfigurationChanged --> handleActivityConfigurationChanged --> performConfigurationChanged

    private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {

        boolean shouldChangeConfig = false;
        if ((activity == null) || (activity.mCurrentConfig == null)) {  
如果activity之前没有configuration,则调用onConfigurationChanged更新
            shouldChangeConfig = true;
        } else {
            int diff = activity.mCurrentConfig.diff(config);
            if (diff != 0) {   如果新的configuration和旧的不一样
                if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {  如果activity的配置中申请了对 diff 的处理,则不重启该activity。
                    shouldChangeConfig = true;
                }
            }
        }

        if (shouldChangeConfig) {  如果不重启activity
            cb.onConfigurationChanged(config);  回调onConfigurationChanged

            if (activity != null) {
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                            "Activity " + activity.getLocalClassName() +
                        " did not call through to super.onConfigurationChanged()");
                }
                activity.mConfigChangeFlags = 0;
                activity.mCurrentConfig = new Configuration(config);  更新activity的当前config为新的config,保证如果下次的config不变的情况下,不会重复调用onConfigurationChanged
            }
        }
    }

0 0
原创粉丝点击