Android平台语言Locale设置流程分析

  Android系统Setting程序中对于语言设置这块的内容。具体位置有以下两处:

  1)、设置显示语言:Settings -> Language & keyboard -> Select language

  2)、设置输入语言:Settings -> Language & keyboard -> Android keyboard [settings] -> Input languages

  Settings工程中,Settings -> Language & keyboard界面所对应的Java代码和Preference布局如下:

  packages/apps/Settings/src/com/android/settings/LanguageSettings.java

  packages/apps/Settings/res/xml/language_settings.xml

  1、Settings -> Language & keyboard -> Select language

  在<android_root>/packages/apps/Settings/res/xml/language_settings.xml中,该模块的Preference布局为:

1 <PreferenceScreen
2   android:key="phone_language"
3   android:title="@string/phone_language">
4   <intent android:action="android.intent.action.MAIN"
5        android:targetPackage="com.android.settings"
6        android:targetClass="com.android.settings.LocalePicker"/>
7 </PreferenceScreen>  

  所以,当用户点击“Settings -> Language & keyboard -> Select language”时,将启动“com.android.settings.LocalePicker”的Activity。其对应的源代码为:
  /packages/apps/Settings/src/com/android/settings/LocalePicker.java
  LocalePicker Activity继承自ListActivity。在它的onCreate()回调中,调用了下面一条语句:
  String[] locales = getAssets().getLocales(); 
  LocalePicker Activity将取得的locale字符串进行了一些处理,然后创建了ArrayAdapter<Loc> adapter,并绑定到ListActivity的ListView上。当用户点击ListView上的Item时,再将选中的locale信息设置到 Android系统中。 选中处理:

 1 @Override
 2 public void onLocaleSelected(final Locale locale) {
 3     if (Utils.hasMultipleUsers(getActivity())) {
 4         mTargetLocale = locale;
 5         showDialog(DLG_SHOW_GLOBAL_WARNING);
 6     } else {
 7         getActivity().onBackPressed();
 8         LocalePicker.updateLocale(locale);
 9     }
10 }

  处理交给framework/base/core/java/com/android/internal/app/LocalePicker.java处理:

 1 /**
 2  * Requests the system to update the system locale. Note that the system looks halted
 3  * for a while during the Locale migration, so the caller need to take care of it.
 4  */
 5 public static void updateLocale(Locale locale) {
 6     try {
 7         IActivityManager am = ActivityManagerNative.getDefault();
 8         Configuration config = am.getConfiguration();
 9
10         // Will set userSetLocale to indicate this isn‘t some passing default - the user
11         // wants this remembered
12         config.setLocale(locale);
13
14         am.updateConfiguration(config);
15         // Trigger the dirty bit for the Settings Provider.
16         BackupManager.dataChanged("com.android.providers.settings");
17     } catch (RemoteException e) {
18         // Intentionally left blank
19     }
20 }

  这里先将Locale信息更新到配置文件Configuration config = am.getConfiguration()中,再通过am.updateConfiguration(config)进行实质的处理。

  IActivityManager的实现在ActivityManagerNative中,而ActivityManagerNative是一个abstract类,其实现在ActivityManagerService中。如下关系:
  public abstract class ActivityManagerNative extends Binder implements IActivityManager{}
  public final class ActivityManagerService extends ActivityManagerNative{}

  这里有两个updateConfiguration()方法,分别在ActivityManagerNative.ActivityManagerProxy和ActivityManagerService中:

 1 //ActivityManagerNative.ActivityManagerProxy:
 2 public void updateConfiguration(Configuration values) throws RemoteException
 3 {
 4     Parcel data = Parcel.obtain();
 5     Parcel reply = Parcel.obtain();
 6     data.writeInterfaceToken(IActivityManager.descriptor);
 7     values.writeToParcel(data, 0);
 8     mRemote.transact(UPDATE_CONFIGURATION_TRANSACTION, data, reply, 0);
 9     reply.readException();
10     data.recycle();
11     reply.recycle();
12 }
13
14
15 //ActivityManagerService:
16 public void updateConfiguration(Configuration values) {
17     enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
18             "updateConfiguration()");
19
20     synchronized(this) {
21         if (values == null && mWindowManager != null) {
22             // sentinel: fetch the current configuration from the window manager
23             values = mWindowManager.computeNewConfiguration();
24         }
25
26         if (mWindowManager != null) {
27             mProcessList.applyDisplaySize(mWindowManager);
28         }
29
30         final long origId = Binder.clearCallingIdentity();
31         if (values != null) {
32             Settings.System.clearConfiguration(values);
33         }
34         updateConfigurationLocked(values, null, false, false);
35         Binder.restoreCallingIdentity(origId);
36     }
37 }

  如上是典型的Binder结构,LocalePicker.java调用Client端的代理,即:
  (1),LocalePicker.updateConfiguration()-->ActivityManagerNative.ActivityManagerProxy.updateConfiguration()
  (2),mRemote.transact(UPDATE_CONFIGURATION_TRANSACTION, data, reply, 0),这里的mRemote即Server端,即:ActivityManagerNative和ActivityManagerService
  (3),mRemote.transact()交给ActivityManagerNative.onTransact(int code, Parcel data, Parcel reply, int flags)
  (4),case UPDATE_CONFIGURATION_TRANSACTION: {updateConfiguration(config)}
  (5),updateConfiguration(config)即ActivityManagerNative.updateConfiguration(config),实现在ActivityManagerService.updateConfiguration(config)

  直接看ActivityManagerService.updateConfiguration(config):

 1 public void updateConfiguration(Configuration values) {
 2     enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
 3             "updateConfiguration()");
 4
 5     synchronized(this) {
 6         if (values == null && mWindowManager != null) {
 7             // sentinel: fetch the current configuration from the window manager
 8             values = mWindowManager.computeNewConfiguration();
 9         }
10
11         if (mWindowManager != null) {
12             mProcessList.applyDisplaySize(mWindowManager);
13         }
14
15         final long origId = Binder.clearCallingIdentity();
16         if (values != null) {
17             Settings.System.clearConfiguration(values);
18         }
19         updateConfigurationLocked(values, null, false, false);
20         Binder.restoreCallingIdentity(origId);
21     }
22 }

  这里处理交给:updateConfigurationLocked(values, null, false, false)

 1 boolean updateConfigurationLocked(Configuration values,
 2         ActivityRecord starting, boolean persistent, boolean initLocale) {
 3     // do nothing if we are headless
 4     if (mHeadless) return true;
 5
 6     int changes = 0;
 7
 8     if (values != null) {
 9         Configuration newConfig = new Configuration(mConfiguration);
10         changes = newConfig.updateFrom(values);
11         if (changes != 0) {
12             EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
13
14             if (values.locale != null && !initLocale) {
15                 saveLocaleLocked(values.locale,
16                                  !values.locale.equals(mConfiguration.locale),
17                                  values.userSetLocale, values.simSetLocale);    /// M: sim locale feature
18             }
19
20             mConfigurationSeq++;
21             if (mConfigurationSeq <= 0) {
22                 mConfigurationSeq = 1;
23             }
24             newConfig.seq = mConfigurationSeq;
25             mConfiguration = newConfig;
26             final Configuration configCopy = new Configuration(mConfiguration);
27             mShowDialogs = shouldShowDialogs(newConfig);
28
29             AttributeCache ac = AttributeCache.instance();
30             if (ac != null) {
31                 ac.updateConfiguration(configCopy);
32             }
33             mSystemThread.applyConfigurationToResources(configCopy);
34
35             if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
36                 Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
37                 msg.obj = new Configuration(configCopy);
38                 mHandler.sendMessage(msg);
39             }
40
41             for (int i=mLruProcesses.size()-1; i>=0; i--) {
42                 ProcessRecord app = mLruProcesses.get(i);
43                 try {
44                     if (app.thread != null) {
45                         if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
46                                 + app.processName + " new config " + mConfiguration);
47                         app.thread.scheduleConfigurationChanged(configCopy);
48                     }
49                 } catch (Exception e) {
50                 }
51             }
52             Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
53             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
54                     | Intent.FLAG_RECEIVER_REPLACE_PENDING
55                     /*| Intent.FLAG_RECEIVER_FOREGROUND*/); // downgrade to background
56             broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
57                     null, AppOpsManager.OP_NONE, false, false, MY_PID,
58                     Process.SYSTEM_UID, UserHandle.USER_ALL);
59             if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
60                 intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
61                 //intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); // downgrade to background
62                 broadcastIntentLocked(null, null, intent,
63                         null, null, 0, null, null, null, AppOpsManager.OP_NONE,
64                         false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
65             }
66         }
67     }
68
69     boolean kept = true;
70     final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
71     if (changes != 0 && starting == null) {
72         starting = mainStack.topRunningActivityLocked(null);
73     }
74
75     if (starting != null) {
76         kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
77         mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
78     }
79     if (values != null && mWindowManager != null) {
80         mWindowManager.setNewConfiguration(mConfiguration);
81     }
82     return kept;
83 }

  updateConfigurationLocked中主要做了两件事:
  (1),改变现在的 configuration(这是一个系统配置的类,有兴趣的可以去了解下);
  (2),确保所有正在运行的Activity都运行改变后的configuration。下面可以看看他到底是怎么完成这两件事的。
  首先,通过updateFrom(values)判断是不是真的语言发生了变化,如果改变了,从if条件走,在if里面,前面做一些判断之类的工作,到此也完成了第一步的工作。最重要的是for循环里面的操作,首先得到了所有运行过的app的集合,然后对每个app调用scheduleConfigurationChanged()方法,进行语言的切换工作。

  scheduleConfigurationChanged是在ActivityThread中,这个方执行了 updatePendingConfiguration(config)和 queueOrSendMessage(H.CONFIGURATION_CHANGED, config)两个方法。前面一个方法是更新Configuration;最主要的操作在queueOrSendMessage()里面的handleConfigurationChanged((Configuration)msg.obj, null)方法中。

  接着对handleConfigurationChanged进行分析,从中我们不难发现applyConfigurationToResourcesLocked()这个是一个重新配置资源的函数,performConfigurationChanged(callbacks.get(i), config)这个方法是执行Configuration的改变。即最终完成语言的切换。

  详细的分析下applyConfigurationToResourcesLocked做了哪些工作,updateFrom(config) 把config更新到Configuration中,后面 最主要的是在while () 中做了资源更新和删除就资源的操作。

  performConfigurationChanged方法中,这是完成语言切换的最后一步了,首先判断当前activity的config和新的config是否一样,如果是一样什么都不做;如果不一样,则重启app,重新加载资源达到切换语言。

  总结语言切换的大概流程是,判断configuration中的local即语言是不是有改变,如果有改变即为要切换语言。执行切换语言的时候,对那些已经运行过的程序,执行一个资源的清除和重新加载的过程,就完成了整个系统的语言切换。

  

时间: 2024-08-11 09:57:11

Android平台语言Locale设置流程分析的相关文章

Android 4.4 音量调节流程分析(一)

最近在做Android Audio方面的工作,有需求是在调节Volume_Up_Key & Volume_Down_key时,Spearker or Headset每音阶的衰减变为3db左右.所以利用Source Insight分析Android源码中音量控制的流程,如有错误,欢迎指正,谢谢! 以下是调节音量的流程: Step_1.首先在调节机台Volume_Up_Key & Volume_Down_Key操作时,系统会调用到AudioManager.java中handleKeyUp &a

Android 4.4 音量调节流程分析(二)

之前在Android 4.4 音量调节流程分析(一)里已经有简单的分析音量控制的流程,今天想接着继续分析下音量大小计算的方法.对于任一播放文件而言其本身都有着固定大小的音量Volume_Max,而在AudioPolicyManagerBase.cpp文件中音量调节可以理解为在Volume_Max的基础上乘以系数κ(0≤κ≤1). 现在对AudioPolicyManagerBase.cpp中volIndexToAmpl函数做具体分析,volIndexToAmpl的函数定义如下: 1 float A

Android-语言设置流程分析

Android手机语言切换行为,是通过设置-语言和输入法-语言来改变手机的语言,其实这个功能很少被用户使用. 以Android5.1工程源码为基础,从设置app入手来分析和学习语言切换的过程: 一.语言设置界面: 首先在设置app中找到语言设置这个Preference,目前设置中界面大多都是Fragment,先找到语言和输入法的PreferenceScreen,与其对应的Fragment是InputMethodAndLanguageSettings.java,在其onCreate()方法中,首先

【读书笔记】Android平台的漏洞挖掘和分析

最近比较关注移动端的安全,以后也打算向安卓平台的安全发展.这篇博文主要是记录一些研究Android安全的读书笔记. Fuzzing技术的核心是样本生成技术 测试Android平台的组件间通信功能使用的方法是,首先通过逆向分析或是动态监控获得Intent的数据格式,再去根据格式生成样本.生成样本后发送给待测试组件. 至今为止,Fuzzing依然是最有效的.最多产的漏洞挖掘方法.其他的那些高大上的方法反倒不如Fuzzing. 目前为止,在Android平台上的Fuzzing工具仅有Peach作为一个

Android视图状态及重绘流程分析,带你一步步深入了解View(三)

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17045157 在 前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程,了解了视图绘制流程中onMeasure.onLayout.onDraw这三个最 重要步骤的工作原理,那么今天我们将继续对View进行深入探究,学习一下视图状态以及重绘方面的知识.如果你还没有看过我前面一篇文章,可以先去阅读 Android视图绘制流程完全解析,带你一步步深入了解View(二) .

(转)Android视图状态及重绘流程分析,带你一步步深入了解View(三)

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17045157 在前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程,了解了视图绘制流程中onMeasure.onLayout.onDraw这三个最重要步骤的工作原理,那么今天我们将继续对View进行深入探究,学习一下视图状态以及重绘方面的知识.如果你还没有看过我前面一篇文章,可以先去阅读 Android视图绘制流程完全解析,带你一步步深入了解View(二) . 相信

Android wpa_supplicant 四次握手 流程分析

记录wpa_supplicant四次握手的过程. 相关log:https://www.cnblogs.com/helloworldtoyou/p/9633603.html 接收到第一次握手,会设置一个认证超时时间,根据情况,设置成10s或者70s. 四次握手,如果出错,将会等待这个超时时间后,才会判断认证失败.然后从新连接. wpa_supplicant/wpa_supplicant.c void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_

Cocos2d-x3.3RC0的Android编译Activity启动流程分析

本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXV4aWt1b18x/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" > watermark/2/text/aHR0cDov

Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析

本篇博客旨在分析Android中广播相关的源码流程. 一.基础知识 广播(Broadcast)是一种Android组件间的通信方式. 从本质上来看,广播信息的载体是intent.在这种通信机制下,发送intent的对象就是广播发送方,接收intent的对象就是广播接收者. 在Android中,为广播接收者定义了一个单独的组件:BroadcastReceiver. 1 BroadcastReceiver的注册类型 在监听广播前,要将BroadcastReceiver注册到系统中. Broadcas