Android Kotlin开发:如何在应用关闭后仍保留用户设置偏好
Android Settings Fragment 偏好持久化实现示例
推荐使用Jetpack Preferences DataStore实现,相比旧版SharedPreferences,DataStore支持异步操作、事务处理、不会阻塞主线程,更适合持久化轻量配置。
1. 引入依赖
在模块级build.gradle中添加DataStore依赖:
dependencies { // Preferences DataStore implementation "androidx.datastore:datastore-preferences:1.0.0" // 协程基础依赖 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" }
2. 封装偏好管理工具类
创建单例工具类统一管理读写逻辑:
import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map // 全局扩展方法初始化DataStore,存储文件名为app_settings private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "app_settings") class SettingsPrefManager private constructor(private val context: Context) { // 定义存储字段的key private val SELECTED_LANGUAGE = stringPreferencesKey("selected_language") private val SELECTED_THEME = stringPreferencesKey("selected_theme") companion object { @Volatile private var INSTANCE: SettingsPrefManager? = null // 单例获取方法 fun getInstance(context: Context): SettingsPrefManager { return INSTANCE ?: synchronized(this) { val instance = SettingsPrefManager(context.applicationContext) INSTANCE = instance instance } } } // 保存选中的语言编码 suspend fun saveLanguage(langCode: String) { context.dataStore.edit { preferences -> preferences[SELECTED_LANGUAGE] = langCode } } // 读取选中的语言,默认返回简体中文 val selectedLanguage: Flow<String> = context.dataStore.data .map { preferences -> preferences[SELECTED_LANGUAGE] ?: "zh-CN" } // 保存选中的主题模式 suspend fun saveTheme(themeMode: String) { context.dataStore.edit { preferences -> preferences[SELECTED_THEME] = themeMode } } // 读取选中的主题,默认返回跟随系统 val selectedTheme: Flow<String> = context.dataStore.data .map { preferences -> preferences[SELECTED_THEME] ?: "system" } }
3. Settings Fragment中调用逻辑
在设置页面选择配置后触发保存,页面初始化时读取已有配置渲染UI:
import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.Spinner import androidx.appcompat.app.AppCompatDelegate import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch class SettingsFragment : Fragment() { private lateinit var prefManager: SettingsPrefManager private lateinit var languageSpinner: Spinner private lateinit var themeSpinner: Spinner override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_settings, container, false) prefManager = SettingsPrefManager.getInstance(requireContext()) languageSpinner = view.findViewById(R.id.spinner_language) themeSpinner = view.findViewById(R.id.spinner_theme) // 初始化下拉选择器的选项 initSpinners() // 读取已保存的配置,设置选择器默认选中项 lifecycleScope.launch { prefManager.selectedLanguage.collect { langCode -> val position = (languageSpinner.adapter as ArrayAdapter<String>).getPosition(langCode) languageSpinner.setSelection(position) } } lifecycleScope.launch { prefManager.selectedTheme.collect { themeMode -> val position = (themeSpinner.adapter as ArrayAdapter<String>).getPosition(themeMode) themeSpinner.setSelection(position) } } // 语言选择改变时保存配置 languageSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { val selectedLang = parent?.getItemAtPosition(position) as String lifecycleScope.launch { prefManager.saveLanguage(selectedLang) // 保存后重启Activity生效语言配置 requireActivity().recreate() } } override fun onNothingSelected(parent: AdapterView<*>?) = Unit } // 主题选择改变时保存配置 themeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { val selectedTheme = parent?.getItemAtPosition(position) as String lifecycleScope.launch { prefManager.saveTheme(selectedTheme) // 全局设置主题即时生效 when(selectedTheme) { "light" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) "dark" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) "system" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) } } } override fun onNothingSelected(parent: AdapterView<*>?) = Unit } return view } private fun initSpinners() { // 给下拉选择器填充可选选项,可根据业务调整 val languageOptions = arrayOf("zh-CN", "en-US", "ja-JP") languageSpinner.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, languageOptions) val themeOptions = arrayOf("system", "light", "dark") themeSpinner.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, themeOptions) } }
4. 应用启动时加载全局配置
自定义Application类,启动时读取配置直接全局生效,避免每次打开应用重新走选择逻辑:
import android.app.Application import androidx.appcompat.app.AppCompatDelegate import kotlinx.coroutines.runBlocking class MyApp : Application() { override fun onCreate() { super.onCreate() val prefManager = SettingsPrefManager.getInstance(this) // 启动时读取主题配置直接生效 runBlocking { val theme = prefManager.selectedTheme.first() when(theme) { "light" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) "dark" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) "system" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) } } // 多语言配置同理,此处读取存储的语言编码后设置应用全局Locale即可 } }
注意需要在
AndroidManifest.xml的application标签中添加android:name=".MyApp"指定自定义Application类,否则配置不会生效。
内容的提问来源于stack exchange,提问作者Melom
相关产品推荐
相关产品推荐

