[英]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 的ApplicationListener
的onCreate()
方法中,我需要创建一个 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.