一个基于 ContentProvider 的轻量级 Android KV 存储库,支持跨进程通信和 Xposed 模块集成。
- 简洁的 Kotlin 属性委托 API
- 支持跨进程数据同步
- 支持基本数据类型(String、Int、Long、Boolean、Float、Double、StringSet)
- 支持 Xposed 模块集成
- 基于 Koin 依赖注入
- 基于 SharedPreferences 实现
- library: 核心 KV 存储库
- app: 示例应用
- xposed: Xposed 模块实现
dependencies {
implementation("com.github.xihan123:kv-storage:<JitPack-Version>")
}class MyApp : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MyApp)
androidLogger()
}
// 目标应用包名
KVSyncManager.setTargetPackages("website.xihan.kv.storage")
}
}// 在初始化位置获取
val application = param.args[0] as? Application ?: return
startKoin {
androidContext(application)
androidLogger()
}
HostKVManager.init(enableSharedPreferencesCache = true, modulePackageName = BuildConfig.APPLICATION_ID)<provider
android:name="website.xihan.kv.KVContentProvider"
android:authorities="website.xihan.kv"
android:enabled="true"
android:exported="true" />// 创建 KV 实例
object ModuleConfig : IKVOwner by KVOwner("SHARED_SETTINGS") {
var switchEnable by kvBool()
var textViewText by kvString("default")
var count by kvInt(0)
var ratio by kvDouble(1.0)
}
// 读写数据
ModuleConfig.switchEnable = true
ModuleConfig.textViewText = "123456"
// 检查键是否存在
if (ModuleConfig.containsKV("switchEnable")) { ... }
// 删除单个键
ModuleConfig.removeKV("count")
// 批量设置
ModuleConfig.putAllKV(mapOf(
"switchEnable" to true,
"textViewText" to "hello",
"count" to 100
))
// 获取所有数据
val allData = ModuleConfig.getAllKV()
// 清空所有数据
ModuleConfig.clearAllKV()// 存储数据
KVStorage.putBoolean("SHARED_SETTINGS", "switchEnable", true)
KVStorage.putString("SHARED_SETTINGS", "textViewText", "123456")
KVStorage.putDouble("SHARED_SETTINGS", "ratio", 3.14)
// 读取数据
val text = KVStorage.getString("SHARED_SETTINGS", "textViewText")
val enabled = KVStorage.getBoolean("SHARED_SETTINGS", "switchEnable")
// 检查和删除
if (KVStorage.contains("SHARED_SETTINGS", "textViewText")) {
KVStorage.remove("SHARED_SETTINGS", "textViewText")
}
// 批量操作
KVStorage.putAll("SHARED_SETTINGS", mapOf(
"switchEnable" to true,
"textViewText" to "123456",
"count" to 100,
"ratio" to 3.14
))
// 清除所有数据
KVStorage.clearAll("SHARED_SETTINGS")val helper = HostKVManager.createKVHelper("SHARED_SETTINGS")
// 读取数据
val textViewText = helper.getString("textViewText")
val switchEnable = helper.getBoolean("switchEnable")
val ratio = helper.getDouble("ratio")
// 写入数据
helper.putString("textViewText", "hello")
helper.putBoolean("switchEnable", false)
// 检查和删除
if (helper.contains("textViewText")) {
helper.remove("textViewText")
}
// 批量操作
helper.putAll(mapOf(
"switchEnable" to true,
"textViewText" to "123456"
))
// 添加变化监听
helper.addChangeListener("switchEnable") { key, value ->
Log.d("KV", "$key changed")
}
// 清除缓存
helper.clearCache()
// 清空所有数据
helper.clearAll()一次 IPC 调用查询多个键值,性能提升 10-100 倍:
// 宿主端
val keys = setOf("swithchEnable", "textViewText")
val map = HostKVManager.createKVHelper().getBatch(keys)支持跨进程文件 URI 传输,自动处理权限授予和文件复制。
// 在 Activity 中选择文件并保存 URI
private val filePickerLauncher = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri: Uri? ->
uri?.let {
contentResolver.takePersistableUriPermission(it, Intent.FLAG_GRANT_READ_URI_PERMISSION)
KVFileTransfer.saveFileUri("website.xihan.kv.storage", it)
}
}
// 触发文件选择
filePickerLauncher.launch(arrayOf("*/*"))
// 读取已保存的 URI
val savedUri = KVStorage.getString("SHARED_SETTINGS", "fileUri")// 创建文件接收器
val fileReceiver = KVFileReceiver(application)
// 接收文件(一次性)
fileReceiver.receiveFile()?.let { file ->
Log.d(TAG, "File saved to: ${file.absolutePath}")
}
// 监听文件变化(自动接收)
fileReceiver.observeFile { file ->
Log.d(TAG, "File updated: ${file.absolutePath}")
}
// 自定义目标目录
val customDir = File(application.filesDir, "downloads")
fileReceiver.receiveFile(targetDir = customDir)问题: 使用时抛出 KoinApplicationNotStartedException
解决方案: 在使用前必须初始化
问题: 在 Xposed 模块中修改数据后,宿主应用未收到更新
解决方案:
- 确保已调用
HostKVManager.init()初始化广播接收器 - 设置目标包名:
KVSyncManager.setTargetPackages("com.example.host") - 检查广播权限是否正常
- 检查目标应用是否已安装且运行
问题: 宿主端无法通过 ContentProvider 读取数据
解决方案:
- 确保模块端已在 AndroidManifest.xml 中注册
KVContentProvider - 检查 authority 是否为
website.xihan.kv - 确保模块应用已安装且运行
- 清除目标应用数据
问题: 读取数据时类型转换异常
解决方案: 确保读写使用相同的数据类型方法
// 错误示例
KVStorage.putInt("settings", "value", 123)
val result = KVStorage.getString("settings", "value") // 类型不匹配
// 正确示例
KVStorage.putInt("settings", "value", 123)
val result = KVStorage.getInt("settings", "value")问题: 宿主端无法读取文件 URI,抛出 SecurityException
解决方案:
- 确保模块端使用
KVFileTransfer.saveFileUri()保存 URI(自动授予权限) - 确保模块端已调用
takePersistableUriPermission() - 检查目标应用包名是否正确
// 正确示例
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
KVFileTransfer.saveFileUri("com.example.host", uri)⚠️ 必须先初始化才能使用 KVStorage 和 HostKVManager⚠️ 跨进程通信依赖 ContentProvider,确保模块应用已安装和已打开⚠️ 不建议读写大量数据(建议单个 KV < 1MB)⚠️ 所有操作已线程安全,但频繁跨进程调用仍有性能开销⚠️ 文件传输需要模块端授予 URI 权限,使用KVFileTransfer.saveFileUri()自动处理