文章

多语言切换

多语言切换

多语言切换 - 重启 App

目标:和微信类似,在设置界面打开切换语言的界面,选择语言后重启 HomeActivity,语言切换完成,下次重新打开 App ,也是用户设置的语言。

多语言切换注意

ApplicationContext/Activity/Resources.getSystem() 区别

  1. Application/Activity 的 Locale 是分开的要分别设置 Locale;Resources.getSystem() 是跟随系统语言的
  2. 某些手机中,弹出 Toast 时如果使用的是 getApplicationContext(),弹出的语言是系统默认的语言,所以最好都传 Activity 的 Context

全局 Context/全局 Resource 的引用(单例/枚举)

1
2
3
4
5
6
public class ResUtils {
    private static Resources res = GlobalContext.getAppContext().getResources();
    public static String getStr(@StringRes int resID) {
        return res.getString(resID);
    }
}

工具类中全局缓存了 Resource 了,那么在不杀进程的重启切换这部分语言资源是切换不过来的。

  1. 单例
1
2
3
4
5
6
// 单例
object ObjTest {
    val name: String = ResUtils.getStr(R.string.title_tab_recommend)
    val name1: String =
        GlobalContext.getAppContext().resources.getString(R.string.title_tab_recommend)
}
  1. 枚举
1
2
3
4
// 枚举
enum class EnumTest(val s: String) {
    ONE(ResUtils.getStr(R.string.title_tab_recommend));
}
  1. 全局引用 Resource
1
2
3
public class ResUtils {
    private static Resources res = GlobalContext.getApplication().getResources();
}

部分手机需要给 Local 设置语言还有国家才成效,所以最好都设置国家或地区

失效 -WebView 加载会重置语言设置 (Android7.0 及 +)

你的 app 加载了 WebView 你会发现语言又变回了系统默认的默认语言

在 Android 7 之前 WebView 的渲染是通过 Android System webView 来实现的。但是在 Android7 之后 WebView 会被作为一个应用程序的方式服务于各个三方 APP。由于 WebView 这里是作为一个单独的应用程序,所以他不会被绑定到你自己 APP 设置的 Local 上。不仅如此,WebView 还会把语言变成设备的 Local 设置。然后相应的资源文件也会被变成设备语言下的资源文件这样就导致了只要打开了含有 WebView 的页面,应用内语言设置就失效的问题。

  • 解决 1 切换语言前 WebView.destroy()
1
2
3
4
5
6
7
8
9
10
11
12
13
//处理Android7(N)WebView 导致应用内语言失效的问题
public static void destoryWebView(Context context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        new WebView(context).destroy();
    }
}
// 切换语言
Resources resources = context.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
Configuration config = resources.getConfiguration();
config.locale = getLocaleByType(type);
LogUtils.logd("setLocale: " + config.locale.toString());
resources.updateConfiguration(config, dm);
  • 解决 2:在 WebView 加载后还需要设置一遍语言。(App 中 WebView 是单例的不能销毁的)

在 app 启动时就加载一次 WebView ,然后在设置语言,只要 WebView 第一次加载后修改了语言,后面再加载便不会重置为系统语言。

1
2
3
4
5
6
7
8
9
10
11
12
public class AppApplication extends CommonApplication {
    @Override
    public void onCreate() {
        super.onCreate();
        Looper.myQueue().addIdleHandler(() -> {
            LogUtils.i("webView preload.");
            WebViewPreLoader.getInstance().preLoad(getApplicationContext());
            MultiLangUtils.applyLanguage(getApplicationContext(), MultiLangUtils.getUserSettingLocale(getApplicationContext()), null);
            return false;
        });
    }
}
  • 注意:如果是调用的 addIdleHandler,时机不明确,容易导致 Locale 被重置。需要把调用时机放到明确的时机或者在 onActivityResume 重新设置下用户保存的语言 Locale
1
2
3
4
Looper.myQueue().addIdleHandler(() -> {
    WebViewPreLoader.getInstance().preLoad(getApplicationContext());
    return false;
});

失效 - 切换系统语言/横竖屏切换(屏幕旋转)

  1. 系统切换语言,会把 Activity/ApplicationContext 的 Locale 更改
  2. 系统切换语言会走 Application 的 onConfigurationChanged,所以需要在这个方法中再设置一遍语言
1
2
3
4
5
6
7
class BaseApplication : Application() {
    @Override
    public void onConfigurationChanged(@NotNull Configuration newConfig) {
        MultiLangUtils.applyLanguage(this, MultiLangUtils.getUserSettingLocale(this), null);
        super.onConfigurationChanged(newConfig);
    }
}

失效 - 使用微信开源的热修复框架 Tinker,打了包含资源的补丁之后会导致多语言失效

如果打了包含资源 string 文件的补丁之后,会导致多语言失效,本来选的繁体变成了简体语言,同时无论你怎么切换语言,都没有生效。这属于 Tinker 的 bug,已经有人在 Tinker 的 github 主页上反馈了,但是这个 issue 任然没有关闭:https://github.com/Tencent/tinker/issues/302

Toolbar 或者 ActionBar 的 title 切换语言不起作用

默认 title 是从 AndroidManifest.xml 中 Activity 的 label 标签里读取的,我们在代码里手动设置一下 title 即可

主题用的是:

1
Theme.AppCompat.Light.DarkActionBar

手动设置

1
2
3
4
//Toolbar
toolbar.setTitle(R.string.app_name);
//ActionBar
actionBar.setTitle(R.string.title_activity_settings);

Activity 和 V7#AppcompatActivity 的返回 Locale 区别

只有 values-zhvalues-en 资源,语言设置:“中文简体 → 日语 → 英语”

  1. 用 Activity
1
2
3
LocaleList.getDefault()        : zh_CN_#Hans,ja_JP,en_US,
Configuration.getLocales()     : en_US,zh_CN_#Hans,ja_JP,
LocaleList.getAdjustedDefault(): en_US,zh_CN_#Hans,ja_JP,
  1. 当项目引用了 AppCompat-v7 包后(即便你的所有 Activity 继承的仍然只是原生的 Activity,而非 AppCompatActivity)
1
2
3
LocaleList.getDefault()        : zh_CN_#Hans,ja_JP,en_US,
Configuration.getLocales()     : zh_CN_#Hans,ja_JP,en_US,
LocaleList.getAdjustedDefault(): zh_CN_#Hans,ja_JP,en_US,

返回的都是系统实际的语言列表

大概 google 在这个包里做了处理,屏蔽了系统根据应用提供的资源调整语言列表的功能,相当于让一切回到 7.0 以前的版本。


多语言切换

什么是 Locale

Locale 是 JavaSE 中一个类,用以表示本地语言的类型,可用于日历、数字和字符串的本地化。

  • Locale 由下面五个部分组成:
字段含义格式示例
language国际现有的语言表示2 或 3 个字母,皆小写zh- 中文 (拼音缩写),en-english
country(region)国家或地区国家 2 个字母 (大写),区域 3 数字CN- 中国,US- 美国,030-Eastern Asia(东亚)
script区分语言或其方言书写形式的脚本4 个字母,首字母大写其余小写Hans- 简体中文,Hant- 繁体中文,Latn- 拉丁文
variant其他可用子标签未涵盖的语言或其方言的语言变体字母开头至少 5 位,数字开头至少 4 位pinyin- 须有前缀 zh-Latn
extensions从单个字符键到字符串值的映射扩展2-8 字母或数字ca-japanese(Japanese Calendar)
  • 创建 Locale 的两种方式:
1
2
3
4
5
6
// 传入语言生成Locale,country与variant为空
Locale(String language)
// 语言+国家,variant为空
Locale(String language, String country)
// 语言+国家+variant
Locale(String language, String country, String variant)
  • 通过 Builder 构建
1
2
// 通过设置各个字段来构建Locale,这种方式比构造函数要精确,并且会判断传入的值是否符合Locale类定义的语法要求
Locale aLocale = new Builder().setLanguage("zh").setScript("Hans").setRegion("CN").build();
  • 遍历系统中存在的所有 Locale
1
2
3
4
Locale[] locales = Locale.getAvailableLocales();
for (Locale locale : locales) {
    System.out.println("语言:"+locale.getDisplayLanguage()+",国家:"+locale.getDisplayCountry()+","+locale);
}

注:Locale.getScript() 方法是在 Android API21 中才新增的。

添加多语言文件(在 res 资源文件目录下添加不同语言的 values)

values 文件夹的命名规则如下

  1. 语言通过由两个字母组成的 ISO 639-1 语言代码定义,可以选择后跟两个字母组成的 ISO 3166-1-alpha-2 区域码(前带小写字母 “r”)。
  2. 这些代码不区分大小写;r 前缀用于区分区域码。 不能单独指定区域。

示例:values-zh,values-zh-rCN,values-zh-rTW,values-en-rUS

values 匹配规则

当应用启动的时候,系统会根据当前的语言环境自动去匹配对应的 values 文件夹,匹配规则如下:

  1. 7.0 之前,先匹配与当前应用 Configuration 语言一致的资源 (language,country 相同),如没有再匹配 language 一致的资源 (命名中只有 language,如 values-en),如无则使用默认资源。
  2. 7.0 之后,系统语言设置中可添加多个语言,优先匹配规则与上述一样,不过添加了可匹配同一语言不同国家的资源,即 language 与 country 都没匹配上,也可匹配同一个 language 但不同 country 的资源,即是同一父项下的不同子项。
    如果第一语言没有对应资源匹配,可继续查找匹配第二位的语言,这就是语言列表的作用。如果列表中的语言都没匹配上,则使用默认资源。
  3. 特别注意点: 简体中文与繁体中文不是同一体系的
    • 示例 1:语言设置为简体中文,没有 values-zh-rCN 的资源,即使有 values-zh-rTW 或者 values-zh-rHK 的资源也不会使用,而会使用默认的资源。
    • 示例 2: 语言设置为繁体中文,没有 values-zh-rTW 的资源,即使有 values-zh 或 values-zh-rCN 的资源也不会使用,而会使用默认资源。

在不同的 value 文件夹下(例如 valuevalue-envalues-zh-rTW 文件夹)添加不同语言的 string.xml 文件,如图:
a8m0m

Android7.0 之后资源如何匹配

通过修改 Configuration 中的 locale 来实现 app 语言的切换(区分 Android7.0+/Android7.0 以下)

Configuration 包含了设备的所有的配置信息,这些配置信息会影响应用获取的资源。例如 string 资源,就是根据 Configuration 的 locale 属性来判断该取哪种语言的 string 资源,默认是 value 文件夹下的。

  1. Android7.0 及以前版本,Configuration 中的语言相当于是 App 的全局设置
1
2
3
4
5
6
7
8
9
10
11
12
public static void changeAppLanguage(Context context, Locale newLocale){
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
     configuration.setLocale(newLocale);
    } else {
     configuration.locale = newLocale;
    }
    // updateConfiguration
    DisplayMetrics dm = resources.getDisplayMetrics();
    resources.updateConfiguration(configuration, dm);
}

如果你需要设置的语言没有预设值,你可以自己新建一个 Locale 对象(如土耳其 Locale("tr", "TR"));跟随系统设置是 Locale.getDefault()

  1. Android7.0 及之后版本,使用了 LocaleList,Configuration 中的语言设置可能获取的不同,而是生效于各自的 Context。这会导致:Android7.0 使用就的方式,有些 Activity 可能会显示为手机的系统语言。
  2. Android7.0 优化了对多语言的支持,废弃了 updateConfiguration() 方法,替代方法:createConfigurationContext(), 而返回的是 Context。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fun changeLanguage(context: Context, newUserLocale: Locale) {
    val resources: Resources = context.resources
    val dm: DisplayMetrics = resources.displayMetrics
    val config: Configuration = resources.configuration
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        val localeList = LocaleList(newUserLocale)
        LocaleList.setDefault(localeList)
        config.setLocales(localeList)
        Locale.setDefault(newUserLocale)
        resources.updateConfiguration(config, dm) // 不加这句切换不成功
        context.createConfigurationContext(config)
    } else {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            config.setLocale(newUserLocale)
        } else {
            config.locale = newUserLocale
        }
        resources.updateConfiguration(config, dm)
    }
}

重启到 HomeActivity(是否杀进程重启?)

  • 不进进程重启
1
2
3
Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
getActivity().startActivity(intent);

正常来说这段代码应该是没问题的,但是假如你的 App 存在某个 activity 和当前设置页 activity 不在一个 task 栈内的话(比如你从某个通知页用 FLAG_ACTIVITY_NEW_TASK 启动的一个 activity),就不会应用语言设置。因此可以直接杀掉当前 App 的进程,保证是 “ 整个 “ 重启了:

  • 杀进程重启
1
2
3
4
5
6
Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
// 杀掉进程
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);

持久化存储语言设置 (进程重启、屏幕旋转等配置更新)

当你杀掉应用,重新打开,发现设置又失效了。这是因为应用重启后会读取设备默认的 Configuration 信息,其中和语言相关的 locale 属性也会变成默认值,也就是你在系统设置中选择的语言。

  1. App 进程重启失效,需要重新设置用户选择的语言
  2. 屏幕旋转等导致的 onConfigurationChanged 调用,需要重新设置用户选择的语言
1
2
3
4
5
6
7
8
9
10
11
12
class BaseApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        MultiLangUtils.init(this)
    }
    
    // onConfigurationChanged 用于适配横竖屏切换。因为横竖屏切换属于系统配置信息的更新,此时Android会更新ApplicationContext中的Resource对象(ApplicationContext对象并未新建,只是更新了其中的Resource对象)
    override fun onConfigurationChanged(newConfig: Configuration?) {
        MultiLangUtils.applyLanguage(this, MultiLangUtils.getUserSettingLocale(this))
        super.onConfigurationChanged(newConfig)
    }
}

核心工具类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
object MultiLangUtils {

    const val TAG = "locale"

    /**
     * 保存SharedPreferences的文件名
     */
    private const val LOCALE_FILE = "LOCALE_FILE"

    /**
     * 保存Locale的key
     */
    private const val LOCALE_KEY = "LOCALE_KEY"

    private val gson = Gson()

    @JvmStatic
    fun init(app: Application) {
        val userSettingLocale = getUserSettingLocale(app)
        if (needUpdateLocale(app, userSettingLocale)) {
            LogUtils.d(TAG, "init 需要更新语言为($userSettingLocale), app=$app")
            changeLanguage(app, userSettingLocale)
        } else {
            LogUtils.w(TAG, "init 不需要更新语言当前语言($userSettingLocale), app=$app")
        }
        app.registerActivityLifecycleCallbacks(object :
                EmptyActivityLifecycleCallbacks {
                override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                    super.onActivityCreated(activity, savedInstanceState)
                    LogUtils.d(TAG, "------>>onActivityCreated context=$activity, locale=${activity.getLocale()}")
                    val settingLocale = getUserSettingLocale(activity)
                    if (!needUpdateLocale(activity, settingLocale)) {
                        LogUtils.w(TAG, "------>>onActivityCreated applyLanguage 不需要更新语言当前语言为($settingLocale), context=$activity")
                    } else {
                        LogUtils.d(TAG, "------>>onActivityCreated applyLanguage 需要更新语言为($settingLocale), context=$activity")
                        changeLanguage(activity, settingLocale)
                        LogUtils.d(TAG, "------>>onActivityCreated 需要更新语言后,locale=${activity.getLocale()}, userSettingLocale=${getUserSettingLocale(activity)}")
                    }
                }

                override fun onActivityResumed(activity: Activity) {
                    super.onActivityResumed(activity)
                    LogUtils.d(TAG, "------->>onActivityResumed context=$activity, locale=${activity.getLocale()}")
                    val settingLocale = getUserSettingLocale(activity)
                    if (!needUpdateLocale(activity, settingLocale)) {
                        LogUtils.w(TAG, "------->>onActivityResumed applyLanguage 不需要更新语言当前语言为($settingLocale), context=$activity")
                    } else {
                        LogUtils.i(TAG, "------->>onActivityResumed applyLanguage 需要更新语言为($settingLocale), context=$activity")
                        changeLanguage(activity, settingLocale)
                        LogUtils.i(TAG, "------->>onActivityResumed 需要更新语言后,locale=${activity.getLocale()}, userSettingLocale=${getUserSettingLocale(activity)}")
                    }
                }
            })
    }

    /**
     * 获取系统的Locale
     */
    private fun getSystemLocale(): Locale {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0有多语言设置获取顶部的语言
            Resources.getSystem().configuration.locales[0]
        } else {
            Resources.getSystem().configuration.locale
        }
    }

    /**
     * 获取用户设置的Locale
     *
     * @param context Context
     * @return Locale
     */
    @JvmStatic
    fun getUserSettingLocale(context: Context): Locale {
        val sp = context.getSharedPreferences(LOCALE_FILE, Context.MODE_PRIVATE)
        val savedLocaleJson = sp.getString(LOCALE_KEY, "")
        if (savedLocaleJson.isNullOrBlank()) {
            LogUtils.w(TAG, "LocaleSwitchUtils#getUserSettingLocale(savedLocaleJson=null),获取getCurrentLocale(${getCurrentLocale(context)}), context=$context")
            return getCurrentLocale(context)
        }
        LogUtils.d(TAG, "LocaleSwitchUtils#getUserSettingLocale(获取用户设置的Locale)=$savedLocaleJson, context=$context")
        return jsonToLocale(savedLocaleJson)
    }

    /**
     * 获取当前的Locale
     *
     * @param context Context
     * @return Locale
     */
    fun getCurrentLocale(context: Context): Locale {
        val locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0有多语言设置获取顶部的语言
            context.resources.configuration.locales[0]
        } else {
            context.resources.configuration.locale
        }
        LogUtils.d(TAG, "LocaleSwitchUtils#getCurrentLocale(获取当前的Locale)=$locale, context=$context")
        return locale
    }

    /**
     * 保存用户设置的Locale
     *
     * @param context Context
     * @param locale  Locale
     */
    private fun saveUserSettingLocale(context: Context, locale: Locale) {
        val sp =
            context.getSharedPreferences(LOCALE_FILE, Context.MODE_PRIVATE)
        val edit = sp.edit()
        val localeToJson =
            localeToJson(locale)
        val isSuccess = edit.putString(LOCALE_KEY, localeToJson).commit()
        LogUtils.e(TAG, "LocaleSwitchUtils#saveUserSettingLocale(保存用户设置的Locale)=$locale, context=$context, isSuccess=$isSuccess")
    }

    /**
     * Locale转成json
     *
     * @param locale UserLocale
     * @return json String
     */
    private fun localeToJson(locale: Locale): String {
        return gson.toJson(locale)
    }

    /**
     * json转成Locale
     *
     * @param pLocaleJson LocaleJson
     * @return Locale
     */
    private fun jsonToLocale(pLocaleJson: String?): Locale {
        return gson.fromJson(pLocaleJson, Locale::class.java)
    }

    fun applySystemLanguage(context: Context, activityClassName: String? = null) {
        val systemLocale = getSystemLocale()
        if (!needUpdateLocale(context, systemLocale) &&
            !needUpdateLocale(context.applicationContext, systemLocale)
        ) {
            return
        }
        changeLanguage(
            context.applicationContext,
            systemLocale
        )
        changeLanguage(context, systemLocale)
        saveUserSettingLocale(
            context,
            systemLocale
        )
        if (!activityClassName.isNullOrBlank()) {
            restartActivity(
                context,
                activityClassName
            )
        }
    }

    @JvmStatic
    fun applyLanguage(
        context: Context,
        newUserLocale: Locale,
        activityClassName: String? = null,
        saveConfig: Boolean = false
    ) {
        if (!needUpdateLocale(context, newUserLocale) &&
            !needUpdateLocale(context.applicationContext, newUserLocale)
        ) {
            LogUtils.w(TAG, "applyLanguage 不需要更新语言 ${getUserSettingLocale(context)}, context=$context")
            return
        }

        changeLanguage(
            context.applicationContext,
            newUserLocale
        )

        changeLanguage(
            context,
            newUserLocale
        )

        if (saveConfig) {
            saveUserSettingLocale(
                context,
                newUserLocale
            )
        }

        if (!activityClassName.isNullOrBlank()) {
            restartActivity(
                context,
                activityClassName
            )
        }
    }

    /**
     * 设置语言类型
     */
    fun changeLanguage(context: Context, newUserLocale: Locale) {
        val resources: Resources = context.resources
        val dm: DisplayMetrics = resources.displayMetrics
        val config: Configuration = resources.configuration
        LogUtils.e(TAG, "changeLanguage修改语言为:$newUserLocale,context=$context")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val localeList = LocaleList(newUserLocale)
            LocaleList.setDefault(localeList)
            config.setLocales(localeList)
            Locale.setDefault(newUserLocale)
            resources.updateConfiguration(config, dm) // 不加这句切换不成功
            context.createConfigurationContext(config)
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                config.setLocale(newUserLocale)
            } else {
                config.locale = newUserLocale
            }
            resources.updateConfiguration(config, dm)
        }
        LogUtils.e(TAG, "changeLanguage语言后当前语言为:${getCurrentLocale(context)},期望语言为:$newUserLocale,context=$context")
    }

    /**
     * 判断需不需要更新
     *
     * @param context       Context
     * @param newUserLocale New User Locale
     * @return true / false
     */
    private fun needUpdateLocale(context: Context, newUserLocale: Locale?): Boolean {
        return newUserLocale != null && !getCurrentLocale(context).isSameLanguage(newUserLocale)
    }

    /**
     * 是否是设置值
     *
     * @return 是否是设置值
     */
    fun isSetValue(context: Context): Boolean {
        return needUpdateLocale(
            context,
            getUserSettingLocale(context)
        )
    }

    /**
     * 重启当前Activity
     */
    private fun restartActivity(context: Context, clazz: Class<out Activity?>?) {
        val intent = Intent(context, clazz)
        if (context !is Activity) {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK)
        context.startActivity(intent)
    }

    /**
     * 重启当前Activity
     */
    private fun restartActivity(context: Context, activityClassName: String) {
        val intent = Intent()
        intent.component = ComponentName(context, activityClassName)
        if (context !is Activity) {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK)
        context.startActivity(intent)
    }
}

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class BaseApplication : Application() {
    companion object {
        const val TAG = "locale"
    }

    override fun onCreate() {
        super.onCreate()
        MultiLangUtils.init(this)
    }

    override fun onConfigurationChanged(newConfig: Configuration?) {
        Log.i(TAG, "BaseApplication onConfigurationChanged newConfig=${newConfig}")
        MultiLangUtils.applyLanguage(
            this,
            MultiLangUtils.getUserSettingLocale(this),
            saveConfig = true
        )
        super.onConfigurationChanged(newConfig)
    }
}

如果有 webview 预加载的功能,需要在加载 webview 后再次设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class AppApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Looper.myQueue().addIdleHandler(() -> {
            WebViewPreLoader.getInstance().preLoad(getApplicationContext());
            Context context = ForegroundCallbacks.get().currentActivity();
            if (context == null) {
                context = getApplicationContext();
            }
            MultiLangUtils.applyLanguage(context, MultiLangUtils.getUserSettingLocale(getApplicationContext()), null, false);
            return false;
        });
    }
}

Ref

android7.0 切换注意

详解解释了 Android6.0 和 7.0 系统加载资源的流程差异

  • updateConfiguration 和 createConfigurationContext

替换 updateConfiguration https://github.com/captain-miao/MultiLanguagesSwitch https://www.cnblogs.com/Sharley/p/9155824.html

无需重启 App 语言切换

https://github.com/MichaelJokAr/MultiLanguages

其他

本文由作者按照 CC BY 4.0 进行授权