SharedPreferences #

一、SharedPreferences概述 #

SharedPreferences是Android提供的一种轻量级数据存储方式,用于存储少量的键值对数据。它使用XML文件存储数据,适合存储简单的配置信息、用户偏好设置等。

1.1 特点 #

  • 轻量级:适合存储少量数据
  • 键值对:以键值对形式存储
  • 持久化:数据保存在文件中
  • 简单易用:API简单直观

1.2 适用场景 #

  • 用户偏好设置
  • 应用配置信息
  • 简单的用户数据
  • 登录状态保存

1.3 不适用场景 #

  • 大量数据存储
  • 复杂的数据结构
  • 频繁的读写操作
  • 需要事务支持的场景

二、基本使用 #

2.1 获取SharedPreferences #

kotlin
// 方式1:通过Context获取
val sharedPreferences = getSharedPreferences("my_prefs", Context.MODE_PRIVATE)

// 方式2:通过Activity获取(文件名为当前Activity类名)
val sharedPreferences = getPreferences(Context.MODE_PRIVATE)

// 方式3:通过PreferenceManager获取(已废弃)
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)

2.2 存储数据 #

kotlin
val sharedPreferences = getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()

// 存储各种类型的数据
editor.putString("name", "张三")
editor.putInt("age", 25)
editor.putBoolean("isLogin", true)
editor.putFloat("score", 95.5f)
editor.putLong("timestamp", System.currentTimeMillis())
editor.putStringSet("tags", setOf("Android", "Kotlin"))

// 提交更改
editor.apply()  // 异步提交
// 或
editor.commit() // 同步提交,返回是否成功

2.3 读取数据 #

kotlin
val sharedPreferences = getSharedPreferences("my_prefs", Context.MODE_PRIVATE)

// 读取数据,提供默认值
val name = sharedPreferences.getString("name", "")
val age = sharedPreferences.getInt("age", 0)
val isLogin = sharedPreferences.getBoolean("isLogin", false)
val score = sharedPreferences.getFloat("score", 0f)
val timestamp = sharedPreferences.getLong("timestamp", 0L)
val tags = sharedPreferences.getStringSet("tags", emptySet())

2.4 删除数据 #

kotlin
val editor = sharedPreferences.edit()

// 删除指定键
editor.remove("name")

// 清空所有数据
editor.clear()

editor.apply()

2.5 检查键是否存在 #

kotlin
val containsName = sharedPreferences.contains("name")

// 获取所有键值对
val allEntries = sharedPreferences.all

三、apply vs commit #

特性 apply() commit()
执行方式 异步 同步
返回值 Boolean
性能 较好 较差
使用场景 大多数情况 需要确认是否成功
kotlin
// 推荐:异步提交
editor.apply()

// 需要确认是否成功时使用
val success = editor.commit()
if (success) {
    // 保存成功
} else {
    // 保存失败
}

四、封装工具类 #

kotlin
object SPUtils {
    private const val FILE_NAME = "app_prefs"
    private lateinit var sharedPreferences: SharedPreferences
    
    fun init(context: Context) {
        sharedPreferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
    }
    
    fun put(key: String, value: Any?) {
        val editor = sharedPreferences.edit()
        when (value) {
            is String -> editor.putString(key, value)
            is Int -> editor.putInt(key, value)
            is Boolean -> editor.putBoolean(key, value)
            is Float -> editor.putFloat(key, value)
            is Long -> editor.putLong(key, value)
            is Set<*> -> editor.putStringSet(key, value as Set<String>)
        }
        editor.apply()
    }
    
    fun getString(key: String, defaultValue: String = ""): String {
        return sharedPreferences.getString(key, defaultValue) ?: defaultValue
    }
    
    fun getInt(key: String, defaultValue: Int = 0): Int {
        return sharedPreferences.getInt(key, defaultValue)
    }
    
    fun getBoolean(key: String, defaultValue: Boolean = false): Boolean {
        return sharedPreferences.getBoolean(key, defaultValue)
    }
    
    fun getFloat(key: String, defaultValue: Float = 0f): Float {
        return sharedPreferences.getFloat(key, defaultValue)
    }
    
    fun getLong(key: String, defaultValue: Long = 0L): Long {
        return sharedPreferences.getLong(key, defaultValue)
    }
    
    fun remove(key: String) {
        sharedPreferences.edit().remove(key).apply()
    }
    
    fun clear() {
        sharedPreferences.edit().clear().apply()
    }
    
    fun contains(key: String): Boolean {
        return sharedPreferences.contains(key)
    }
}

五、监听数据变化 #

kotlin
class MainActivity : AppCompatActivity() {
    
    private val listener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
        when (key) {
            "name" -> {
                val newName = sharedPreferences.getString(key, "")
                Log.d("SP", "Name changed to: $newName")
            }
            "isLogin" -> {
                val isLogin = sharedPreferences.getBoolean(key, false)
                Log.d("SP", "Login status: $isLogin")
            }
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val sharedPreferences = getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
        sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        val sharedPreferences = getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
        sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener)
    }
}

六、存储复杂对象 #

6.1 使用JSON #

kotlin
// 存储对象
fun putObject(key: String, obj: Any) {
    val json = Gson().toJson(obj)
    sharedPreferences.edit().putString(key, json).apply()
}

// 读取对象
inline fun <reified T> getObject(key: String): T? {
    val json = sharedPreferences.getString(key, null) ?: return null
    return Gson().fromJson(json, T::class.java)
}

// 使用
data class User(val id: Int, val name: String)

val user = User(1, "张三")
putObject("user", user)

val savedUser: User? = getObject<User>("user")

6.2 使用序列化 #

kotlin
fun putSerializable(key: String, obj: Serializable) {
    val baos = ByteArrayOutputStream()
    val oos = ObjectOutputStream(baos)
    oos.writeObject(obj)
    val base64 = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT)
    sharedPreferences.edit().putString(key, base64).apply()
}

@Suppress("UNCHECKED_CAST")
fun <T : Serializable> getSerializable(key: String): T? {
    val base64 = sharedPreferences.getString(key, null) ?: return null
    val bytes = Base64.decode(base64, Base64.DEFAULT)
    val bais = ByteArrayInputStream(bytes)
    val ois = ObjectInputStream(bais)
    return ois.readObject() as T
}

七、多进程支持 #

7.1 MODE_MULTI_PROCESS(已废弃) #

kotlin
// 已废弃,不推荐使用
val sharedPreferences = getSharedPreferences("my_prefs", Context.MODE_MULTI_PROCESS)

7.2 使用ContentProvider #

对于多进程数据共享,推荐使用ContentProvider或其他IPC机制。

八、最佳实践 #

8.1 使用常量管理键名 #

kotlin
object SPKeys {
    const val USER_NAME = "user_name"
    const val USER_AGE = "user_age"
    const val IS_LOGIN = "is_login"
    const val THEME_MODE = "theme_mode"
}

// 使用
SPUtils.put(SPKeys.USER_NAME, "张三")
val name = SPUtils.getString(SPKeys.USER_NAME)

8.2 避免存储敏感数据 #

kotlin
// 错误:存储敏感数据
editor.putString("password", "123456")
editor.putString("token", "xxx")

// 正确:使用EncryptedSharedPreferences
val masterKey = MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

val sharedPreferences = EncryptedSharedPreferences.create(
    context,
    "secret_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

8.3 避免频繁写入 #

kotlin
// 错误:频繁写入
for (i in 0..100) {
    editor.putInt("key_$i", i).apply()
}

// 正确:批量写入
for (i in 0..100) {
    editor.putInt("key_$i", i)
}
editor.apply()

8.4 使用DataStore替代 #

对于新项目,推荐使用Jetpack DataStore:

kotlin
// 添加依赖
dependencies {
    implementation("androidx.datastore:datastore-preferences:1.0.0")
}

// 创建DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

// 存储数据
val KEY_NAME = stringPreferencesKey("name")
val KEY_AGE = intPreferencesKey("age")

scope.launch {
    dataStore.edit { preferences ->
        preferences[KEY_NAME] = "张三"
        preferences[KEY_AGE] = 25
    }
}

// 读取数据
val nameFlow: Flow<String> = dataStore.data.map { preferences ->
    preferences[KEY_NAME] ?: ""
}

九、总结 #

本章详细介绍了SharedPreferences:

  1. SharedPreferences的基本概念和特点
  2. 数据的增删改查操作
  3. apply与commit的区别
  4. 工具类封装
  5. 监听数据变化
  6. 存储复杂对象
  7. 最佳实践

SharedPreferences适合存储简单的配置数据,对于复杂数据或大量数据,应考虑使用数据库或其他存储方式。

最后更新:2026-03-26