【问题标题】:Accessing class variables from a Kotlin coroutine从 Kotlin 协程访问类变量
【发布时间】:2018-12-04 21:35:24
【问题描述】:

我在这里遇到了一个奇怪的问题(Kotlin 和协程的新手)。我正在修改函数 getMovies 中的类变量。在启动块中,我得到一个 JSON,然后我通过 GSON 运行它并得到一个可变的电影信息条目列表。您可以从 Anko 看到 2 个显示 Toast 的 longToast,但第一个显示 20,另一个显示 0。这是为什么呢? Android Studio 不会抛出任何错误,我引用的是同一个 moviesList 变量。我在互联网上尝试了许多博客和说明,但找不到任何有用的东西。任何帮助将不胜感激!

class MainActivity : Activity() {

private var moviesList: MutableList<Movie> = mutableListOf()

fun getMovies() {
        launch(UI){
            val result = async(CommonPool){
                getResponseJSON()
            }.await()
            moviesList = Gson().fromJson(result, MovieDBResponse::class.java).results
            longToast(moviesList.size.toString())
        }
        longToast(moviesList.size.toString())
    }
}

【问题讨论】:

  • 你确定 first 吐司真的显示20吗?也许尝试写入日志以确保顺序。
  • 你还在使用实验性协程 API 吗?是时候升级到 Kotlin 1.3 了。另外,不要使用async-await,这是一种反模式。请改用withContext(Dispatchers.IO)
  • @MichaelButscher 是的,第一个肯定显示 20。我尝试了日志,但由于某种原因它们没有显示。
  • @MarkoTopolnik 嗯,会尝试的。任何有用的链接?谢谢!

标签: android kotlin kotlin-coroutines


【解决方案1】:

您的代码是针对实验性协程 API 编写的,所以让我先将其升级到 Kotlin 1.3 并修复一些明显的错误:

class MainActivity : Activity(), CoroutineScope {
    override val coroutineContext = Dispatchers.Main + SupervisorJob()

    private val moviesList: MutableList<Movie> = mutableListOf()

    fun getMovies() {
        launch {
            val result = withContext(Dispatchers.IO) { getResponseJSON() }
            moviesList += Gson().fromJson(result, MovieDBResponse::class.java).results
            longToast("Size after fetching: ${moviesList.size}")
        }
        longToast("Immediate size: ${moviesList.size}")
    }

    override fun onDestroy() {
        super.onDestroy()
        coroutineContext[Job]!!.cancel()
    }
}

现在,看看你的代码,这是我期望它做的:

  1. launch 一个并发协程,最终会从网络中获取一些 JSON 数据
  2. 请求显示“即时大小”吐司
  3. JSON 结果到达
  4. 填充电影列表
  5. 请求显示“获取后的大小”toast

当您在屏幕上看到 toast 时的确切细节可能会有所不同,具体取决于 Android 的政策和您的抓取速度。

【讨论】:

  • 代码看起来很棒!我回家后会试一试并告诉你。并感谢您的逐步解释,非常感谢。但我认为协程允许在单独的(如后台)线程上运行长时间运行的任务,然后在运行时暂停 UI,所以它会以某种顺序工作?那么先启动块,然后再启动 longToast?
  • 暂停 UI 线程正是协程让您以优雅且无中断的方式避免的事情。特别是launch,就像Thread.start(),其中的代码与调用它的代码同时运行。顺便说一句,“同时”not 暗示“在另一个线程上”。许多协程可以通过协作多线程在单个 UI 线程上并发运行。
  • 我试过你的代码,它工作!我看到我可以正确获取数据,并且绝对可以从启动块访问类变量。虽然我看到我肯定需要在操作完成后(在 withContext 块之后)从启动块更新 UI。所以这似乎是合乎逻辑的,因为我们无法真正知道何时检索数据。谢谢您的帮助!将标记为已接受的答案。
  • 这就是 withContext 的作用,将阻塞调用转移到另一个线程,这样它就不会冻结 GUI。当您的协程在 withContext (IO) 中时,其他代码在 GUI 线程上运行。这就是协作多线程。
猜你喜欢
  • 2018-03-04
  • 1970-01-01
  • 1970-01-01
  • 2014-06-24
  • 2018-03-22
  • 1970-01-01
  • 2019-12-04
  • 2022-11-10
  • 1970-01-01
相关资源
最近更新 更多