美文网首页
安卓系统语言切换流程

安卓系统语言切换流程

作者: MickCaptain | 来源:发表于2019-11-03 11:26 被阅读0次

    android system language change 流程

    安卓系统语言切换后的更新过程.jpg

    切换语言核心过程分析

    1. updateLocales 分析
        /**
         * Requests the system to update the list of system locales.
         * Note that the system looks halted for a while during the Locale migration,
         * so the caller need to take care of it.
         */
        public static void updateLocales(LocaleList locales) {
            try {
                final IActivityManager am = ActivityManager.getService();
                final Configuration config = am.getConfiguration();
    
                config.setLocales(locales);
                config.userSetLocale = true;
    
                am.updatePersistentConfiguration(config);
                // Trigger the dirty bit for the Settings Provider.
                BackupManager.dataChanged("com.android.providers.settings");
            } catch (RemoteException e) {
                // Intentionally left blank
            }
        }
    

    updateLocales 会将承载语言变更信息的Locale 信息通过bindle机制传递到AMS
    的updatePersistentConfiguration 中

    1. AMS 中关键操作分析

    经过AMS 中方法的层层调用, 会发现最终走到了updateGlobalConfiguration 这个关键方法中,以下是updateGlobalConfiguration中的以下关键流程

            // 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(mTempConfig);
    
            // We need another copy of global config because we're scheduling some calls instead of
            // running them in place. We need to be sure that object we send will be handled unchanged.
            final Configuration configCopy = new Configuration(mTempConfig);
            if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
                Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
                msg.obj = configCopy;
                msg.arg1 = userId;
                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_CONFIGURATION, "Sending to proc "
                                + app.processName + " new config " + configCopy);
                        app.thread.scheduleConfigurationChanged(configCopy);
                    }
                } 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
                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
            broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
                    AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
                    UserHandle.USER_ALL);
            if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
                intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
                intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
                        | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
                if (initLocale || !mProcessesReady) {
                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                }
                broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
                        AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
                        UserHandle.USER_ALL);
            }
    
            // Override configuration of the default display duplicates global config, so we need to
            // update it also. This will also notify WindowManager about changes.
            performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
                    DEFAULT_DISPLAY);
    
            return changes;
    

    一. mSystemThread.applyConfigurationToResources(mTempConfig)和Settings.System.hasInterestingConfigurationChanges(changes))这两部是用来更新语言配置参说,也就是在此时应用在拿去resource 资源时,拿到的是对应国际化中的资源信息

    二. app.thread.scheduleConfigurationChanged(configCopy);这部是拿到前台应用进程和后台应用进程, 通知对应的应用更新节界面

    1. 界面刷新,显示国际化信息

    AMS 最终调用ActivityThread 中的scheduleConfigurationChanged, 经过一系列流转,最终走到activity中我们熟悉的onConfigurationChanged
    不废话上核心

        /**
         * Decides whether to update an Activity's configuration and whether to inform it.
         * @param activity The activity to notify of configuration change.
         * @param newConfig The new configuration.
         * @param amOverrideConfig The override config that differentiates the Activity's configuration
         *                         from the base global configuration. This is supplied by
         *                         ActivityManager.
         * @param displayId Id of the display where activity currently resides.
         * @param movedToDifferentDisplay Indicates if the activity was moved to different display.
         * @return Configuration sent to client, null if no changes and not moved to different display.
         */
        private Configuration performActivityConfigurationChanged(Activity activity,
                Configuration newConfig, Configuration amOverrideConfig, int displayId,
                boolean movedToDifferentDisplay) {
            if (activity == null) {
                throw new IllegalArgumentException("No activity provided.");
            }
            final IBinder activityToken = activity.getActivityToken();
            if (activityToken == null) {
                throw new IllegalArgumentException("Activity token not set. Is the activity attached?");
            }
    
            boolean shouldChangeConfig = false;
            if (activity.mCurrentConfig == null) {
                shouldChangeConfig = true;
            } else {
                // If the new config is the same as the config this Activity is already running with and
                // the override config also didn't change, then don't bother calling
                // onConfigurationChanged.
                final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
    
                if (diff != 0 || !mResourcesManager.isSameResourcesOverrideConfig(activityToken,
                        amOverrideConfig)) {
                    // Always send the task-level config changes. For system-level configuration, if
                    // this activity doesn't handle any of the config changes, then don't bother
                    // calling onConfigurationChanged as we're going to destroy it.
                    if (!mUpdatingSystemConfig
                            || (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
                            || !REPORT_TO_ACTIVITY) {
                        shouldChangeConfig = true;
                    }
                }
            }
            if (!shouldChangeConfig && !movedToDifferentDisplay) {
                // Nothing significant, don't proceed with updating and reporting.
                return null;
            }
    
            // Propagate the configuration change to ResourcesManager and Activity.
    
            // ContextThemeWrappers may override the configuration for that context. We must check and
            // apply any overrides defined.
            Configuration contextThemeWrapperOverrideConfig = activity.getOverrideConfiguration();
    
            // We only update an Activity's configuration if this is not a global configuration change.
            // This must also be done before the callback, or else we violate the contract that the new
            // resources are available in ComponentCallbacks2#onConfigurationChanged(Configuration).
            // Also apply the ContextThemeWrapper override if necessary.
            // NOTE: Make sure the configurations are not modified, as they are treated as immutable in
            // many places.
            final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
                    amOverrideConfig, contextThemeWrapperOverrideConfig);
            mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig,
                    displayId, movedToDifferentDisplay);
    
            activity.mConfigChangeFlags = 0;
            activity.mCurrentConfig = new Configuration(newConfig);
    
            // Apply the ContextThemeWrapper override if necessary.
            // NOTE: Make sure the configurations are not modified, as they are treated as immutable
            // in many places.
            final Configuration configToReport = createNewConfigAndUpdateIfNotNull(newConfig,
                    contextThemeWrapperOverrideConfig);
    
            if (!REPORT_TO_ACTIVITY) {
                // Not configured to report to activity.
                return configToReport;
            }
    
            if (movedToDifferentDisplay) {
                activity.dispatchMovedToDisplay(displayId, configToReport);
            }
    
            if (shouldChangeConfig) {
                activity.mCalled = false;
                activity.onConfigurationChanged(configToReport);
                if (!activity.mCalled) {
                    throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
                                    " did not call through to super.onConfigurationChanged()");
                }
            }
    
            return configToReport;
        }
    

    ActivityThread 方法 performActivityConfigurationChanged 中让人眼前一亮的 mResourcesManager.updateResourcesForActivityactivity.dispatchMovedToDisplay(displayId, configToReport) ,这里更新了Activity 的资源和界面, 这只是Activity 资源的更新,那么挂载的fragment 和 window 怎么更新的呢,这里调用了activity.onConfigurationChanged(configToReport),看看里边做了什么

        public void onConfigurationChanged(Configuration newConfig) {
            if (DEBUG_LIFECYCLE) Slog.v(TAG, "onConfigurationChanged " + this + ": " + newConfig);
            mCalled = true;
    
            mFragments.dispatchConfigurationChanged(newConfig);
    
            if (mWindow != null) {
                // Pass the configuration changed event to the window
                mWindow.onConfigurationChanged(newConfig);
            }
    
            if (mActionBar != null) {
                // Do this last; the action bar will need to access
                // view changes from above.
                mActionBar.onConfigurationChanged(newConfig);
            }
        }
    

    哈哈, 在Activity onConfigurationChanged 更新了Fragment , Window , ActionBar 界面, 可以快乐的玩耍了

    相关文章

      网友评论

          本文标题:安卓系统语言切换流程

          本文链接:https://www.haomeiwen.com/subject/uerhpctx.html