简体   繁体   English

如何等待 Preferences DataStore 中的数据加载完毕?

[英]How do I wait until data from Preferences DataStore is loaded?

I'm trying to make a LibGDX Live Wallpaper.我正在尝试制作 LibGDX 动态壁纸。 But I think that is unrelated to the problem I'm having at the moment.但我认为这与我目前遇到的问题无关。 Basically, I'm storing the wallpaper's settings inside a Preferences DataStore.基本上,我将墙纸的设置存储在 Preferences DataStore 中。 Now, if you need to retrieve data from the DataStore, you need to retrieve a Kotlin Flow and use it to get the preferences.现在,如果您需要从 DataStore 检索数据,则需要检索 Kotlin Flow并使用它来获取首选项。 Inside LibGDX's ApplicationListener inside the onCreate() method, I need to create a KtxScreen object. I decided to pass all the preferences inside a constructor of my Screen .在 LibGDX 的ApplicationListeneronCreate()方法中,我需要创建一个 KtxScreen object。我决定将所有首选项传递到我的Screen的构造函数中。 However, since a Flow 's data can only be obtained inside a coroutine, I cannot construct my KtxScreen object with the user preferences as a constructor parameter because I need to wait until my Flow emits the values.但是,由于Flow的数据只能在协程内获取,因此我无法使用用户首选项作为构造函数参数来构造我的KtxScreen object,因为我需要等到我的Flow发出值。 I solved this with a very very dirty while (userPreferences == null) Unit piece of code which basically blocks the whole app until the preferences are emitted so I can construct my KtxScreen .我用一段非常非常脏的while (userPreferences == null) Unit代码解决了这个问题,它基本上阻止了整个应用程序,直到发出首选项,这样我才能构建我的KtxScreen What would be a good way to solve this?解决这个问题的好方法是什么?

It is acceptable in libGDX to block the rendering thread.在 libGDX 中阻塞渲染线程是可以接受的。 This is a separate thread from the Android main thread, so it won't freeze the Android UI or put you at risk of an ANR (application not responding error).这是一个独立于 Android 主线程的线程,因此它不会冻结 Android UI 或让您面临 ANR(应用程序未响应错误)的风险。 It will freeze any game/wallpaper rendering while blocking, but that's OK when you're still loading the scene.它会在阻塞时冻结任何游戏/墙纸渲染,但是当您仍在加载场景时这没关系。

So, it would be acceptable to use runBlocking in create() or render() to wait for the first batch of settings.所以,在create()render()中使用runBlocking来等待第一批设置是可以接受的。 Since KtxScreen doesn't use create() , you could block directly at the property initialization.由于 KtxScreen 不使用create() ,您可以直接在属性初始化时阻塞。 You can call first() on a Flow to get just one item from it, which would be fine for initial setup of your KtxScreen class.您可以在 Flow 上调用first()以仅从中获取一个项目,这对于 KtxScreen class 的初始设置来说很好。

If you're using the ktx-async library from libKtx, you can collect your flow on the rendering thread, so then if the user updates the settings while your wallpaper is running, it will safely update your settings.如果您使用的是 libKtx 中的ktx-async库,您可以在渲染线程上收集您的流程,这样如果用户在您的墙纸运行时更新设置,它会安全地更新您的设置。

Here's one example of how you might achieve this (I didn't test it), but there are many acceptable different ways it could be handled.这是您如何实现这一目标的一个示例(我没有测试它),但是有许多可接受的不同方式可以处理它。 Note: this is assuming you instantiate this screen on the rendering thread, not the Android main thread.注意:这是假设您在渲染线程而不是 Android 主线程上实例化此屏幕。 The rendering thread is what is used to call create() on your game class, which is the typical place to instantiate your first screen.渲染线程用于在您的游戏 class 上调用create() ,这是实例化您的第一个屏幕的典型位置。

class MyScreen(
    val settingsFlow: Flow<UserPreferences>
): KtxScreen {

    var prefs: UserPreferences = runBlocking { settingsFlow.first() }

    init {
        settingsFlow
            .drop(1) // the first one should be the value we already got above
                     // unless the user changed them impossibly fast
            .onEach { prefs = it }
            .launchIn(KtxAsync)
    }

    //...
}

If you want to show some sort of initial loading image in your wallpaper, you could use if/else in your render function like this.如果你想在你的壁纸中显示某种初始加载图像,你可以像这样在你的render function 中使用 if/else。 But I don't think it should be necessary because preferences will take probably less than half a second to load.但我不认为这是必要的,因为偏好可能需要不到半秒的时间来加载。

class MyScreen(
    val settingsFlow: Flow<UserPreferences>
): KtxScreen {

    var _prefs: UserPreferences? = null

    init {
        settingsFlow
            .onEach { _prefs = it }
            .launchIn(KtxAsync)
    }

    override fun render() {
        val prefs = _prefs // for the sake of smart-casting
        if (prefs == null) {
            renderLoadingImage()
        } else {
            renderScene(prefs)
        }
    }

    //...

    private fun renderScene(prefs: UserPreferences) {
        //...
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 等待首选项数据存储从 workmanager 中的首选项数据存储中检索数据 - Wait for Preferences Datastore to retrieve data from preferences datastore in workmanager 如何等到从服务器加载数据 - how to wait until data from server is loaded ListView:如何等到它加载完毕? - ListView: how do I wait until it's loaded up? 如何从 Android [Kotlin] 中的 DataStore Preferences 中清除数据 - How to clear the data from DataStore Preferences in Android [Kotlin] 等到加载视图后,再将加载的数据放入其中 - Wait until a View is loaded and then put loaded data in it 如何从 DataStore Preferences 读取字符串? - How to read from DataStore Preferences to string? 如何使Espresso等到Data Binding使用数据模型更新View? - How do I make Espresso wait until Data Binding has updated the View with the data-model? 如何从Observable更新数据,而不必重新订阅/添加可处理对象,而我可以在可以加载数据的情况下延迟layout.xml的渲染? - How do I update data from Observable without having to re-subscribe/add disposable where I can delay the rendering of layout.xml until data is loaded? 尝试从首选项数据存储中读取数据时返回 null - Return null when try to read data from preferences datastore Android 如何等到服务实际连接? - Android how do I wait until a service is actually connected?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM