如何使用异步任务侦听器返回并暂停Coroutine?

发布于 2025-01-21 21:48:39 字数 2272 浏览 0 评论 0 原文

我要做的是以下内容:

  1. 使用调色板从API加载代理图标
  2. ,确定什么是充满活力的Swatch
  3. 设置此背景RGB颜色,然后将其保存到数据库
  4. 数据库中,将更新,更新流程并推动新的流程UI
  5. UI的更新会选择它,然后将其设置为回收器视图,

但是,我正在努力使其在我的存储库中工作。我的功能看起来像这样:

suspend fun updateAgentBackground(agent: Agent, successResult: SuccessResult) {
    if (agent.backgroundRgb == null) {
        withContext(Dispatchers.IO) {
            Palette.Builder(successResult.drawable.toBitmap()).generate { palette ->
                val rgb = palette?.vibrantSwatch?.rgb
                if (rgb != null) {
                    val agentWithBackground = agent.copy(backgroundRgb = rgb)
                    agentDao.insertAgent(agentWithBackground.toAgentEntity())
                }
            }
        }
    }
}

但是,它在 agentdao.intertagent()上在运行时崩溃,因为它说它在主线程上运行了DB操作。我也无法将此方法包裹在 generate()带有 withContext(dispatchers.io)的回调中,因为它不在coroutine主体中。

这就是 palette.builder()。generate()函数在源代码中的样子:

@NonNull
public AsyncTask<Bitmap, Void, Palette> generate(
        @NonNull final PaletteAsyncListener listener) {
    if (listener == null) {
        throw new IllegalArgumentException("listener can not be null");
    }

    return new AsyncTask<Bitmap, Void, Palette>() {
        @Override
        @Nullable
        protected Palette doInBackground(Bitmap... params) {
            try {
                return generate();
            } catch (Exception e) {
                Log.e(LOG_TAG, "Exception thrown during async generate", e);
                return null;
            }
        }

        @Override
        protected void onPostExecute(@Nullable Palette colorExtractor) {
            listener.onGenerated(colorExtractor);
        }
    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap);
}

public interface PaletteAsyncListener {
    void onGenerated(@Nullable Palette palette);
}

因此,我将其传递给它 paletteasynclistener ,它 hurn hurn oster > ongererated()。我以某种方式希望这种方法暂停Coroutine,并以更新的背景返回 Agent 。或以某种方式能够在此回调中调用我的DB方法 AgentDao.Intertagent()在主线程中。

我可以以某种方式混合并匹配源代码如何使用 asynctask 与我的coroutines使用?

What I'm trying to do is the following:

  1. Load an Agent icon from an API
  2. Using Palette, determine what is the vibrant swatch
  3. Set this background RGB color and save it into the database
  4. Database will be updated, updating the flow and pushing the new updates to the UI
  5. UI picks it up, which then gets set into a recycler view

However, I'm struggling to get it working in my repository. My function looks like this:

suspend fun updateAgentBackground(agent: Agent, successResult: SuccessResult) {
    if (agent.backgroundRgb == null) {
        withContext(Dispatchers.IO) {
            Palette.Builder(successResult.drawable.toBitmap()).generate { palette ->
                val rgb = palette?.vibrantSwatch?.rgb
                if (rgb != null) {
                    val agentWithBackground = agent.copy(backgroundRgb = rgb)
                    agentDao.insertAgent(agentWithBackground.toAgentEntity())
                }
            }
        }
    }
}

However, it crashes at runtime on agentDao.insertAgent() because it says it's running the DB operation on the main thread. I also cannot wrap this method within the generate() callback with withContext(Dispatchers.IO) since it's not in a coroutine body.

This is what the Palette.Builder().generate() function looks like in the source code:

@NonNull
public AsyncTask<Bitmap, Void, Palette> generate(
        @NonNull final PaletteAsyncListener listener) {
    if (listener == null) {
        throw new IllegalArgumentException("listener can not be null");
    }

    return new AsyncTask<Bitmap, Void, Palette>() {
        @Override
        @Nullable
        protected Palette doInBackground(Bitmap... params) {
            try {
                return generate();
            } catch (Exception e) {
                Log.e(LOG_TAG, "Exception thrown during async generate", e);
                return null;
            }
        }

        @Override
        protected void onPostExecute(@Nullable Palette colorExtractor) {
            listener.onGenerated(colorExtractor);
        }
    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap);
}

public interface PaletteAsyncListener {
    void onGenerated(@Nullable Palette palette);
}

So I'm passing it the PaletteAsyncListener which just has one function onGenerated(). I somehow want this method to suspend the coroutine, returning to me the Agent with the updated background. Or somehow to be able to call my DB method agentDao.insertAgent() within this callback off the main thread.

Can I somehow mix and match how the source code is using AsyncTask with my coroutines?

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

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

发布评论

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

评论(2

没企图 2025-01-28 21:48:39

asynctask 在API级别30中弃用,请考虑使用Kotlin Coroutines或其他 Kotlin并发实用程序。如果没有 asynctask 它将看起来像这样的东西:

palette.builder class:

suspend fun generatePalette() = withContext(Dispatchers.IO) {
    generate()
}

in储存库中:

suspend fun updateAgentBackground(agent: Agent, successResult: SuccessResult) {
    if (agent.backgroundRgb == null) {
        val palette = Palette.Builder(successResult.drawable.toBitmap()).generatePalette()
        val rgb = palette?.vibrantSwatch?.rgb
        if (rgb != null) {
            val agentWithBackground = agent.copy(backgroundRgb = rgb)
            agentDao.insertAgent(agentWithBackground.toAgentEntity())
        }
    }
}

也在 agentdao 标记函数 insertagent insertagent < /code> as <代码>暂停

suspend fun insertAgent(...)

AsyncTask was deprecated in API level 30, consider using Kotlin Coroutines instead or other Kotlin concurrency utilities. Without AsyncTask it will look something like this:

In Palette.Builder class:

suspend fun generatePalette() = withContext(Dispatchers.IO) {
    generate()
}

In repository:

suspend fun updateAgentBackground(agent: Agent, successResult: SuccessResult) {
    if (agent.backgroundRgb == null) {
        val palette = Palette.Builder(successResult.drawable.toBitmap()).generatePalette()
        val rgb = palette?.vibrantSwatch?.rgb
        if (rgb != null) {
            val agentWithBackground = agent.copy(backgroundRgb = rgb)
            agentDao.insertAgent(agentWithBackground.toAgentEntity())
        }
    }
}

Also in the AgentDao mark function insertAgent as suspend:

suspend fun insertAgent(...)
写下不归期 2025-01-28 21:48:39

如果由于某种原因无法修改调色板的源代码。Builder类,则可以编写这样的暂停适应功能。 sustendCancellableCoroutine 是通过暂停Coroutine并为您直接使用暂停的延续来将基于回调的功能转换为悬挂功能。回调发射时恢复Coroutine,并在恢复之前取消Coroutine,取消工作。

suspend fun Palette.Builder.await(): Palette? = suspendCancellableCoroutine { cont ->
    val task = generate { cont.resume(it) }
    cont.invokeOnCancellation(task.cancel())
}

然后,您的其他函数可以同步写入,并在必要时使用 withContext

suspend fun updateAgentBackground(agent: Agent, successResult: SuccessResult) {
    if (agent.backgroundRgb == null) {
        val palette = Palette.Builder(successResult.drawable.toBitmap()).await()
        val rgb = palette?.vibrantSwatch?.rgb
        if (rgb != null) {
            val agentWithBackground = agent.copy(backgroundRgb = rgb)
            agentDao.insertAgent(agentWithBackground.toAgentEntity())
        }
    }
}

确保您将标记 AgentDao.InterTagent()将其标记为 suppend 功能,因此您可以在Coroutine中的任何地方致电,而不必担心呼叫线程。

上面的代码假定 tobitmap()是非障碍物。如果阻止它,则应用context()将其包裹在适当的中。

If for some reason you cannot modify the source code of the Palette.Builder class, you could write a suspending adapting function like this. suspendCancellableCoroutine is for converting callback-based functions into suspend functions by suspending the coroutine and giving you the suspended continuation to work with directly. Resume the coroutine when the callback fires, and cancel the work if the coroutine is cancelled before it resumed.

suspend fun Palette.Builder.await(): Palette? = suspendCancellableCoroutine { cont ->
    val task = generate { cont.resume(it) }
    cont.invokeOnCancellation(task.cancel())
}

Then your other function can be written in a synchronous way and use withContext where necessary:

suspend fun updateAgentBackground(agent: Agent, successResult: SuccessResult) {
    if (agent.backgroundRgb == null) {
        val palette = Palette.Builder(successResult.drawable.toBitmap()).await()
        val rgb = palette?.vibrantSwatch?.rgb
        if (rgb != null) {
            val agentWithBackground = agent.copy(backgroundRgb = rgb)
            agentDao.insertAgent(agentWithBackground.toAgentEntity())
        }
    }
}

Make sure you mark AgentDao.insertAgent() as a suspend function so you can call if anywhere in a coroutine without worrying about calling thread.

Above code assumes toBitmap() is non-blocking. If it blocks, you should wrap it in an appropriate withContext().

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