You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 18:20:17