Android DataStore Preferences读取失败未抛出IOException问题排查
问题分析与解决方案
核心问题
- 数据源异常被吞:
PinCodeLocalDataSource的catch块仅打印日志但未重新抛出异常,导致下游仓库层无法捕获错误。 - 仓库层Flow未正确收集:原仓库代码中仅调用了
onEach中间操作符,但未执行collect终端操作,导致数据源的Flow从未被启动,既不会触发数据转换,也无法传递异常。
修复步骤
1. 修复数据源的异常传递
修改PinCodeLocalDataSource的getPinCode方法,在catch块中重新抛出异常,确保错误能传递到下游:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "mydocument") val PIN_CODE_KEY = stringPreferencesKey("pin_code") class PinCodeLocalDataSource @Inject constructor( @ApplicationContext private val context: Context ) { private val TAG = "PinCodeLocalDataSource" fun getPinCode(): Flow<List<Int>> { Log.d(TAG, "Pin code status collected") return context.dataStore.data.map { preferences -> Log.d(TAG, "Data store mapped") val pin: String = preferences[PIN_CODE_KEY] ?: "" val intPin: List<Int> = pin.toCharArray().map { it.digitToIntOrNull() ?: 0 } intPin }.catch { e -> Log.e(TAG, "Caught an exception while reading pin: ${e.message}") throw e // 重新抛出异常,让下游捕获 } } suspend fun setPinCode(newCode: List<Int>) { context.dataStore.edit { mutablePreferences -> mutablePreferences[PIN_CODE_KEY] = newCode.joinToString("") { it.toString() } } } }
2. 修复仓库层的Flow收集逻辑
推荐使用Flow链式操作符的写法,更符合Flow的设计习惯:
private val TAG = "PinCodeRepository" fun load(): Flow<Loader<List<Int>>> = dataSource.getPinCode() .onStart { emit(Loader.Loading) } // 加载开始时发射Loading状态 .map { Loader.Success(it) } // 将数据转换为Success状态 .catch { e -> Log.d(TAG, "Loading pin code failed.") if (e is IOException) { emit(Loader.Error) // IO异常发射Error状态 } else { throw e // 非IO异常重新抛出,交由上层处理 } } .flowOn(Dispatchers.IO)
如果偏好手动收集的写法,也可以这样实现:
fun load(): Flow<Loader<List<Int>>> = flow { emit(Loader.Loading) try { // 手动收集数据源Flow,触发数据读取与转换 dataSource.getPinCode().collect { pin -> emit(Loader.Success(pin)) } } catch (e: IOException) { Log.d(TAG, "Loading pin code failed.") emit(Loader.Error) } catch (e: Exception) { throw e // 其他异常重新抛出 } }.flowOn(Dispatchers.IO)
关键说明
- 冷Flow特性:DataStore的
data是冷Flow,只有被收集时才会启动数据读取,原仓库代码未执行collect,导致map中的代码从未触发,也就不会打印Data store mapped日志。 - 异常传递:Flow的
catch操作符会消费异常,必须手动重新抛出才能让下游感知错误,否则异常会被静默处理。
内容的提问来源于stack exchange,提问作者sasetz
相关产品推荐
相关产品推荐

