首页 > 解决方案 > 将数据保存到 OnLifecycleEvent.ON_STOP 中的 Preferences DataStore 永远不会被调用

问题描述

当按下后退按钮时,我很想观察一些数据并将其保存ON_START到共享首选项中。ON_STOP问题是调用了前面的代码preferencesDataStore.dataStore.edit,但该值未保存到首选项中。

这是我的代码。

查看型号:

@HiltViewModel
class MyViewModel @Inject constructor(
    private val repository: Repository
) : ViewModel() {

    private val _myUiState = MutableStateFlow<List<Int>>(emptyList())
    val myUiState: StateFlow<List<Int>> = _myUiState

    // Load data from a suspend fun and mutate state
    init {
        viewModelScope.launch {
            repository.readFlow()
                .collect {
                    _myUiState.value = it
                }
        }
    }

    fun saveDataToPreferences() {
        viewModelScope.launch {
            Log.d(GameApp.TAG, "save data, data=${_myUiState.value}") // this is logged
            repository.saveData(_myUiState.value)
        }
    }
}

分段:

@AndroidEntryPoint
class MyFragment : Fragment(R.layout.fragment_my) {

    private val myModel: MyViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Create a new coroutine in the lifecycleScope
        viewLifecycleOwner.lifecycleScope.launch {
            // repeatOnLifecycle launches the block in a new coroutine every time the
            // lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Trigger the flow and start listening for values.
                // This happens when lifecycle is STARTED and stops
                // collecting when the lifecycle is STOPPED
                myModel.myUiState
                    .collect {
                        // update the UI
                    }
            }
        }

        lifecycle.addObserver(GameFragmentLifecycleObserver(gamePlayViewModel))
    }
}



class MyFragmentLifecycleObserver(private val model: MyViewModel) : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onStop() = model.saveDataToPreferences()
}

数据层:

interface DataSource {

    suspend fun readFlow(): Flow<List<Int>>

    suspend fun saveData(data: List<Int>)
}

internal class LocalDataSource(private val preferencesDataStore: PreferencesDataStore) : DataSource {

    override fun readFlow(): Flow<List<Int>> =
        preferencesDataStore.dataStore.data
            .catch { exception ->
                // dataStore.data throws an IOException when an error is encountered when reading data
                if (exception is IOException) {
                    emit(emptyPreferences())
                } else {
                    throw exception
                }
            }
            .map { preferences ->
                preferences[PreferencesKeys.DATA] ?: emptyList()
            }

    override suspend fun saveData(list: List<Int>) {
        preferencesDataStore.dataStore.edit { preferences ->
            preferences[PreferencesKeys.DATA] = list
        }
    }
}

private const val USER_PREFERENCES_NAME = "user_preferences"
private val Context.dataStore by preferencesDataStore(
    name = USER_PREFERENCES_NAME
)

interface PreferencesDataStore {

    val dataStore: DataStore<Preferences>
}

internal class PreferencesDataStoreImpl(private val context: Context) : PreferencesDataStore {

    override val dataStore: DataStore<Preferences>
        get() = context.dataStore
}

interface Repository {

    fun readFlow(): Flow<List<Int>>

    suspend fun saveData(list: List<Int>)
}

internal class RepositoryImpl @Inject constructor(private val dataSource: DataSource) : Repository {

    override fun readFlow() = dataSource.readFlow()

    override suspend fun saveData(list: List<Int>) = dataSource.saveData(list)
}

刀柄。

@Module
@InstallIn(ViewModelComponent::class)
internal object ViewModelModule {

    @Provides
    @ViewModelScoped
    fun provideRepository(preferencesDataStore: PreferencesDataStore): Repository = RepositoryImpl(LocalDataSource(preferencesDataStore))
}

构建.gradle

ext.hilt_version = "2.35"

def kotlinCoroutinesCore = '1.5.1'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesCore"

// AndroidX
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation "androidx.fragment:fragment-ktx:1.3.6"
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0-alpha03"
implementation "androidx.datastore:datastore-preferences:1.0.0"

// Hilt
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"

我了解我的数据未保存的原因是因为模型视图范围被取消,并且repository.saveData(_myUiState.value)也被取消。

我想知道正确的模式是什么。

标签: androiddagger-hiltandroid-jetpack-datastore

解决方案


我决定采用不同的方法并在获得数据后保存数据,而不是将其保存在内存中,直到屏幕最小化或关闭。

MyFragmentLifecycleObserver可以删除。

更新。

如果您真的需要保存一次数据(当应用程序进入后台时),您可以使用应用程序范围的范围来完成。请阅读文章https://medium.com/androiddevelopers/create-an-application-coroutinescope-using-hilt-dd444e721528


推荐阅读