我如何等到加载来自首选项数据存储的数据?

发布于 2025-01-29 02:59:42 字数 435 浏览 3 评论 0原文

我正在尝试制作LibGDX实时墙纸。但是我认为这与我目前遇到的问题无关。基本上,我将墙纸的设置存储在首选项数据存储中。现在,如果您需要从数据存储中检索数据,则需要检索kotlin flow并使用它来获取首选项。在libgdx的applicationListener increate()方法中,我需要创建一个ktxscreen对象。我决定将所有偏好传递在我的屏幕的构造函数中。但是,由于flow的数据只能在coroutine中获得我的排放值。我用非常脏的解决了此问题,而(userPreferences == null)ume一件代码,基本上会阻止整个应用程序,直到发出偏好,以便我可以构造我的ktxscreen> 。解决这个问题的好方法是什么?

I'm trying to make a LibGDX Live Wallpaper. 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. Now, if you need to retrieve data from the DataStore, you need to retrieve a Kotlin Flow and use it to get the preferences. 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. 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. 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. What would be a good way to solve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

人生戏 2025-02-05 02:59:42

在libgdx中可以接受以阻止渲染线程。这是与Android主线程的单独线程,因此它不会冻结Android UI,也不会使您有ANR的风险(应用程序不响应错误)。它会在阻塞时冻结任何游戏/壁纸渲染,但是当您仍在加载场景时,这还可以。

因此,在中使用runblocking create(create()render()等待第一批设置。由于KTXSCREEN不使用create(),因此您可以在属性初始化中直接阻止。您可以在流程上调用first()从中获得一个项目,这对于您的KTXSCREEN类的初始设置就可以了。

如果您使用的是libktx的ktx-async库,则可以在渲染线程上收集流程,因此,如果用户在运行壁纸时将用户更新设置,则将安全地更新您的设置。

这是您如何实现这一目标的一个例子(我没有测试),但是可以处理许多可接受的不同方式。注意:这是假设您在渲染线程上实例化此屏幕,而不是Android主线程。渲染线程是用于调用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)
    }

    //...
}

如果您想在墙纸中显示某种初始加载图像,则可以在dender函数中使用此类功能。但是我认为这不必是必要的,因为偏好可能会花费不到半秒钟的加载。

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) {
        //...
    }
}

It is acceptable in libGDX to block the rendering thread. 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). 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. Since KtxScreen doesn't use create(), you could block directly at the property initialization. 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.

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.

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. 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 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. 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) {
        //...
    }
}
桜花祭 2025-02-05 02:59:42

如果我们需要从数据存储中获取键值作为同步方式,那么我们需要使用Runblocking阻止线程,以便将数据返回原始数据类型中的数据。

fun getValue(context: Context): Int {

    return runBlocking(Dispatchers.IO) {
    context.dataStore.dataStore.data.first()[EXAMPLE_COUNTER] ?: 0

}}

INCASE以异步方式从数据存储中获取值,然后直接调用将返回流的方法。

fun <T> DataStore<Preferences>.getValueFlow(
key: Preferences.Key<T>,
defaultValue: T ): Flow<T> {
return this.data
    .catch { exception ->
        if (exception is IOException) {
            emit(emptyPreferences())
        } else {
            throw exception
        }}.map { preferences ->
        preferences[key] ?: defaultValue
    }}


    

If we need to fetch the key value from Datastore as synchronous way then we need to block the thread using runBlocking so that will return data in Primitive datatype.

fun getValue(context: Context): Int {

    return runBlocking(Dispatchers.IO) {
    context.dataStore.dataStore.data.first()[EXAMPLE_COUNTER] ?: 0

}}

Incase to fetch value from Datastore as Asynchronous way then directly call map method which will return Flow.

fun <T> DataStore<Preferences>.getValueFlow(
key: Preferences.Key<T>,
defaultValue: T ): Flow<T> {
return this.data
    .catch { exception ->
        if (exception is IOException) {
            emit(emptyPreferences())
        } else {
            throw exception
        }}.map { preferences ->
        preferences[key] ?: defaultValue
    }}


    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文