Android应用按应用语言偏好修改时卡顿问题求助
语言选择器点击后应用冻结问题排查
将另一款应用中运行正常的语言选择器代码移植到新应用后,出现如下问题:进入可用语言列表点击选项时,应用直接冻结,无法返回或选择其他选项;退出重启应用后,语言已成功变更,但再次尝试修改语言仍会触发冻结。
通过Logcat确认语言变更逻辑已执行成功,但界面未及时刷新,日志如下:
- Storing App Locales : app-locales: en persisted successfully.
- Schedule relaunch activity: com.jeluchu.test.MainActivity
相关代码
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application ... android:localeConfig="@xml/locales_config" ... tools:targetApi="tiramisu"> ... <service android:name="androidx.appcompat.app.AppLocalesMetadataHolderService" android:enabled="false" android:exported="false"> <meta-data android:name="autoStoreLocales" android:value="true" /> </service> </application> </manifest>
res/xml/locales_config.xml
<locale-config xmlns:android="http://schemas.android.com/apk/res/android"> <locale android:name="en"/> <locale android:name="es"/> </locale-config>
LanguageSettings.kt
private fun getLanguageNumberByCode(languageCode: String): Int = languageMap.entries.find { it.value == languageCode }?.key ?: 0 fun getLanguageNumber(): Int { return if (Build.VERSION.SDK_INT >= 33) getLanguageNumberByCode( LocaleListCompat.getAdjustedDefault()[0]?.toLanguageTag().toString() ) else getLanguage } @Composable fun getLanguageDesc(language: Int = getLanguageNumber()): String = stringResource( when (language) { ENGLISH -> R.string.la_en_US SPANISH -> R.string.la_es else -> R.string.follow_system } ) private const val ENGLISH = 1 private const val SPANISH = 2 val languageMap: Map<Int, String> = mapOf( ENGLISH to "en", SPANISH to "es" )
LanguageView.kt (Compose)
val language = rememberMutableStateOf(getLanguageNumber()) Scaffold { LazyColumn { item { PreferenceSingleChoiceItem( modifier = Modifier.background(milky), text = R.string.follow_system.toStringRes(), selected = language.value == 0, contentPadding = PaddingValues(horizontal = 12.dp, vertical = 18.dp) ) { onLanguageClick(language, 0) } } languageMap.forEach { languageData -> item { PreferenceSingleChoiceItem( modifier = Modifier.background(milky), text = getLanguageDesc(languageData.key), selected = language.value == languageData.key, contentPadding = PaddingValues(horizontal = 12.dp, vertical = 18.dp) ) { onLanguageClick(language, languageData.key) } } } } }
点击事件与扩展函数
// onLanguageClick 实现 languageMap.getOrElse(PreferencesService.getLanguage) { "" }.setLanguage() selected.value = id // setLanguage 扩展函数 fun String.setLanguage() = AppCompatDelegate.setApplicationLocales( if (isEmpty()) LocaleListCompat.getEmptyLocaleList() else LocaleListCompat.forLanguageTags(this) )
问题根源与修复方案
1. 核心问题
调用AppCompatDelegate.setApplicationLocales()后,系统会异步调度Activity重启以应用新语言,但当前代码存在两个关键问题:
onLanguageClick中错误使用旧的偏好值获取语言码,而非用户点击的选项ID对应的码- 系统重启Activity的调度延迟,导致UI线程因等待状态同步而阻塞冻结
2. 具体修复
(1)修正onLanguageClick逻辑
@Composable fun onLanguageClick(selectedLanguageState: MutableState<Int>, id: Int) { val context = LocalContext.current // 获取点击选项对应的语言码 val languageCode = languageMap.getOrDefault(id, "") // 设置应用语言 languageCode.setLanguage() // 更新本地状态 selectedLanguageState.value = id // 手动触发Activity重启,避免系统调度延迟 (context as Activity).run { finish() startActivity(Intent(context, MainActivity::class.java)) } }
(2)优化Compose状态获取
使用derivedStateOf确保语言状态能响应系统语言变化:
@Composable fun getCurrentLanguageNumber(): Int { return remember { derivedStateOf { if (Build.VERSION.SDK_INT >= 33) { getLanguageNumberByCode( LocaleListCompat.getAdjustedDefault()[0]?.toLanguageTag().toString() ) } else { PreferencesService.getLanguage } } }.value }
在LanguageView中替换状态初始化:
val language by remember { derivedStateOf { getCurrentLanguageNumber() } }
(3)移除不必要的状态同步
由于Activity会重启,重启后会重新获取最新语言状态,可简化状态更新逻辑,避免UI线程阻塞。
内容的提问来源于stack exchange,提问作者Jéluchu
相关产品推荐
相关产品推荐

