You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android DataStore Preferences读取失败未抛出IOException问题排查

问题分析与解决方案

核心问题

  1. 数据源异常被吞:PinCodeLocalDataSource的catch块仅打印日志但未重新抛出异常,导致下游仓库层无法捕获错误。
  2. 仓库层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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 08:19:55