转载请注明:http://blog.csdn.net/ytmfdw/article/details/36669059
android语言切换,网上有很多文章,我看了不下于二十来篇,但内容都是千遍一律的,没有一篇能解决我实质的问题。但手头的项目,需要能切换系统语言,网上的代码一般都是通过
public static void updateLocale(Locale locale) {
try {
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
// Will set userSetLocale to indicate this isn't some passing default - the user
// wants this remembered
config.setLocale(locale);
am.updateConfiguration(config);
// Trigger the dirty bit for the Settings Provider.
BackupManager.dataChanged("com.android.providers.settings");
} catch (RemoteException e) {
// Intentionally left blank
}
}
这样的方式,其实这段代码只是通过代理服务来实现locale设置的,真正实现的,是在
/frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
这个类里,以上的那种方法切换语言,会让电视卡屏3秒,用户体验太差了,(本项目是TV相关的),我看了下康佳的做法,设置系统语言时,要求重启电视,这也太狠了,经过本人几天的钻研,总算是弄出了些眉目,终于使语言切换,不卡屏,还能更新系统语言。
以下是我个人整理的一些相关代码,
(这些资料全是本人手打的,请尊重下程序员的劳动成果)
framework层语言设置,添加方法,不刷新界面
1、实现语言切换的代码在:
/frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
方法:
updateConfiguration();
a.该方法实现了保存语言设置结果,通过调用updateConfigurationLocked(values, null, false, false);来实现
updateConfigurationLocked(values, null, false, false);是个私有的方法,
它通过调用
saveLocaleLocked(values.locale,
!values.locale.equals(mConfiguration.locale),
values.userSetLocale);
这个方法,来实现保存结果,实现代码:SystemProperties.set("user.language", l.getLanguage());
最终结果保存在 /data/property/persist.sys.language 文件中,国家也一样
b.保存结果后,通知所有应用,语言设置更新
通过发送消息,告知所注册的应用,也是在 updateConfigurationLocked(values, null, false, false);
if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
msg.obj = new Configuration(configCopy);
mHandler.sendMessage(msg);
}
c.对所有应用进行更新,同样在 updateConfigurationLocked(values, null, false, false);
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);
}
} catch (Exception e) {
}
}
后面的就不详细说了
2、现在我们要实现的目的是:
语言切换时,只保存设置的结果,而不更新界面
1、修改:/frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
添加一个公开方法
public void updateLanguage(Configuration values) {
if (mHeadless)
return;
int changes = 0;
boolean kept = true;
if (values != null) {
Configuration newConfig = new Configuration(mConfiguration);
changes = newConfig.updateFrom(values);
if (changes != 0) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
Slog.i(TAG, "Updating configuration to: " + values);
}
EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
if (values.locale != null) {
saveLocaleLocked(values.locale,
!values.locale.equals(mConfiguration.locale),
values.userSetLocale);
Slog.i("ytmfdw", "update language::" + values.toString());
}
}
}
}
这个方法,只会把结果保存到 /data/property/persist.sys.language 文件中,如果有国家,也会保存,这个执行过程非常快,不会卡
但界面不会更新,只有重启系统才会更新,因此,更新界面的方法,还得用原来的 updateConfiguration(Configuration values)
2、修改代理接口:
/frameworks/base/core/java/android/app/ActivityManagerNative.java
a.添加updateLanguage方法:
public void updateLanguage(Configuration values) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
values.writeToParcel(data, 0);
mRemote.transact(UPDATE_LANGUAGE_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
b.添加调用接口
在方法
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
…………
}
体中,添加一分支:
case UPDATE_LANGUAGE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
Configuration config = Configuration.CREATOR.createFromParcel(data);
updateLanguage(config);
reply.writeNoException();
return true;
}
c.声明 UPDATE_LANGUAGE_TRANSACTION 常量,updateLanguage(Configuration values) 方法:
修改:/frameworks/base/core/java/android/app/IActivityManager.java
添加:
public void updateLanguage(Configuration values) throws RemoteException;
int UPDATE_LANGUAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+161;(161视情况而定,不能重复)
=============================================================================================================================
以上是底层代码实现,一般语言设置调用代码:
public static void updateLocale(Locale locale) {
try {
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
// Will set userSetLocale to indicate this isn't some passing default - the user
// wants this remembered
config.setLocale(locale);
am.updateConfiguration(config);
// Trigger the dirty bit for the Settings Provider.
BackupManager.dataChanged("com.android.providers.settings");
} catch (RemoteException e) {
// Intentionally left blank
}
}
这是会刷新界面的,经过上面修改后,可以这样调用 ;
public static void updateLanguage(Locale locale) {
try {
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
// Will set userSetLocale to indicate this isn't some passing default - the user
// wants this remembered
config.setLocale(locale);
am.updateLanguage(config);
// Trigger the dirty bit for the Settings Provider.
BackupManager.dataChanged("com.android.providers.settings");
} catch (RemoteException e) {
// Intentionally left blank
}
}
当然,要先编译services.jar,然后编译framework.jar,否则后面的那个方法:updateLanguage会找不到的