如何实现多进程Android EncryptedSharedPreferences?求技术指引
EncryptedSharedPreferences本身不支持MULTI_MODE_PROCESS,谷歌Jetpack Security库也未提供相关配置选项,直接多进程访问会导致文件锁冲突、数据不一致甚至崩溃——因为它的加密实现依赖单进程的文件访问上下文。以下是两种可行的技术方案:
方案一:用ContentProvider封装EncryptedSharedPreferences
ContentProvider是Android官方跨进程通信组件,天然支持多进程访问,可作为统一入口封装EncryptedSharedPreferences的读写操作,避免直接操作文件带来的冲突。
实现步骤:
- 创建EncryptedPreferencesProvider类
class EncryptedPreferencesProvider : ContentProvider() { private lateinit var encryptedPrefs: SharedPreferences override fun onCreate(): Boolean { // 初始化MasterKey,所有进程共用系统密钥库生成的密钥 val masterKey = MasterKey.Builder(context!!) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() // 初始化EncryptedSharedPreferences encryptedPrefs = EncryptedSharedPreferences.create( context!!, "cross_process_encrypted_prefs", masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) return true } // 实现读取逻辑 override fun query( uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String? ): Cursor? { val targetKey = selection ?: return null val value = encryptedPrefs.getString(targetKey, null) // 使用MatrixCursor封装结果返回 val cursor = MatrixCursor(arrayOf("key", "value")) cursor.addRow(arrayOf(targetKey, value)) return cursor } // 实现写入逻辑 override fun update( uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>? ): Int { values ?: return 0 val editor = encryptedPrefs.edit() values.keySet().forEach { key -> when (val value = values[key]) { is String -> editor.putString(key, value) is Int -> editor.putInt(key, value) is Boolean -> editor.putBoolean(key, value) // 按需支持更多数据类型 } } return if (editor.commit()) 1 else 0 } // 其他方法按需实现(insert/delete/getType),此处简化 override fun insert(uri: Uri, values: ContentValues?): Uri? = null override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0 override fun getType(uri: Uri): String? = null }
- 在AndroidManifest中注册Provider
<provider android:name=".EncryptedPreferencesProvider" android:authorities="com.your.package.cross_process_encrypted_prefs" android:exported="false" <!-- 同一应用内进程访问设为false即可,无需对外暴露 --> android:process=":remote" /> <!-- 若需指定进程可添加,不指定则默认主进程 -->
- 多进程中通过ContentResolver访问
- 读取数据:
val resolver = applicationContext.contentResolver val uri = Uri.parse("content://com.your.package.cross_process_encrypted_prefs") resolver.query(uri, null, "auth_token", null, null)?.use { cursor -> if (cursor.moveToFirst()) { val token = cursor.getString(cursor.getColumnIndexOrThrow("value")) // 使用令牌 } }
- 写入数据:
val resolver = applicationContext.contentResolver val uri = Uri.parse("content://com.your.package.cross_process_encrypted_prefs") val values = ContentValues().apply { put("auth_token", "your_secure_token_here") } resolver.update(uri, values, null, null)
注意事项:
- 密钥安全性:每个进程独立初始化MasterKey即可,无需传递密钥,MasterKey基于Android系统密钥库生成,多进程可安全获取。
- 并发控制:ContentProvider内部会自动处理多进程的并发访问,加上EncryptedSharedPreferences的commit操作是原子性的,能保证数据一致性。
- 权限控制:如果是跨应用访问,需设置自定义权限限制访问范围,避免敏感数据泄露。
方案二:使用支持多进程的加密存储替代
如果不想封装ContentProvider,可以直接使用MMKV加密版,它原生支持多进程,同时提供加密功能,实现更简洁:
// 初始化MMKV(全局初始化一次即可) MMKV.initialize(applicationContext) // 获取加密多进程实例 val encryptedMMKV = MMKV.mmkvWithID( "encrypted_token_store", MMKV.MULTI_PROCESS_MODE, "your_32_byte_encryption_key" // 建议使用32位密钥保证AES256加密强度 ) // 写入令牌 encryptedMMKV.putString("auth_token", "your_token") // 读取令牌 val token = encryptedMMKV.getString("auth_token", null)
内容的提问来源于stack exchange,提问作者Anusha
相关产品推荐
相关产品推荐

