等到Kotlin协程在onCreateView()中完成

时间:2018-12-07 08:03:37

标签: kotlin kotlin-coroutines

我在onCreateView中有一个初始化块,其中一些变量是从SharedPreferences,数据库或网络(当前是从SharedPreferences)分配的。

我想用onViewCreated中的这些值更新视图。但是它们会在onCreateView中的协程完成之前以空值更新。如何等待协程在主线程中完成?

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    ...
    GlobalScope.launch(Dispatchers.Main) {
        val job = GlobalScope.launch(Dispatchers.IO) {
            val task = async(Dispatchers.IO) {
                settingsInteractor.getStationSearchCountry().let {
                    countryName = it.name
                }
                settingsInteractor.getStationSearchRegion().let {
                    regionName = it.name
                }
            }
            task.await()
        }
        job.join()
    }
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    country.updateCaption(countryName)
    region.updateCaption(regionName)
}

更新(20-05-2019)

目前,我不使用onViewCreated。我用onCreateonCreateView编写代码。在onCreateView中,我以这种方式访问​​视图:view.country.text = "abc",依此类推。

4 个答案:

答案 0 :(得分:3)

最好使用async而不是launch https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html

创建lateinit val dataLoad: Deferred<Pair<String, String>。在dataLoad = GlobalScope.async(Dispatchers.Defailt) {}或更早的版本中将其初始化为onCreateViewlaunch中的UI范围中的onViewCreated个新协程。在协程val data = dataLoad.await()中等待结果,然后将数据应用于ui

答案 1 :(得分:2)

在您的情况下,您不需要将GlobalScope用作协程上下文(可以,但是根据文档,不建议使用)。您需要一个本地范围:

private var job: Job = Job()
override val coroutineContext: CoroutineContext
    get() = Dispatchers.Main + job

@Override
public void onDestroy() {
    super.onDestroy();
    job.cancel()
}

您的片段还应实现CoroutineScope并在Android中使用Dispatchers.Main来向应用程序的build.gradle添加依赖项:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'

等待协程完成的代码:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    launch {
        val operation = async(Dispatchers.IO) {
            settingsInteractor.getStationSearchCountry().let {
                countryName = it.name
            }
            settingsInteractor.getStationSearchRegion().let {
                regionName = it.name
            }
        }
        operation.await() // wait for result of I/O operation without blocking the main thread

        // update views
        country.updateCaption(countryName)
        region.updateCaption(regionName)
    }
}

答案 2 :(得分:1)

一种实现方法是在主线程中使用启动

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

GlobalScope.launch(Dispatchers.IO) {
     settingsInteractor.getStationSearchCountry().let {
         countryName = it.name
     }
     settingsInteractor.getStationSearchRegion().let {
         regionName = it.name
     }

     launch(Dispatchers.Main) {
         country.updateCaption(countryName)
         region.updateCaption(regionName)
     }
   }
}

答案 3 :(得分:-1)

使用runBlocking。在这种情况下,您可以阻止UI线程,直到协程完成。当然,您要确保它足够快(少于3秒)以不提高ANR。

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    ...
    val (countryName, regionName) = runBlocking {
        // Switch to background thread and continue blocking UI thread.
        withContext(Dispatchers.IO) {
            val countryName = settingsInteractor.getStationSearchCountry().let {
                it.name
            }
            val regionName = settingsInteractor.getStationSearchRegion().let {
                it.name
            }
            countryName to regionName
        }
    }

    // Assign data after runBlocking has finished.
    view.country_name.text = countryName
    view.region_name.text = regionName

    return view
}

我没有测试它,因为在onBackPressed中使用了类似的代码。退出片段(或活动)时,应该在DB中写入一些数据。如果运行太晚,将调用onDestroy并完成协程。在这种情况下,数据将不会写入数据库。因此,我必须停止UI线程,在后台线程中写入DB,然后返回UI。 runBlocking效果很好。