Kotlin Coroutines执行异步加载示例详解
前言 Kotlin Coroutines是Kotlin推出的新的异步API。并不是解决所有问题的最优方案,但是希望在许多情况下它会使事情变得更容易一些。这里只简单的展示一下这个库在安卓中的具体使用方案。下面话不多说了,来一起看看详细的介绍吧。 引入Coroutines //在application的build.gradle文件中的android节点添加如下的代码 kotlin { experimental { coroutines 'enable' } } //添加下面两行到依赖中 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.20" 第一个Coroutines示例 通常我们加载一张图片到ImageView中,异步的加载任务如下所示: fun loadBitmapFromMediaStore(imageId: Int,imagesBaseUri: Uri): Bitmap { val uri = Uri.withAppendedPath(imagesBaseUri,imageId.toString()) return MediaStore.Images.Media.getBitmap(contentResolver,uri) } 这个方法必须在后台线程中执行,因为他属于一个IO操作,这意味着我们有很多解决方案可以启动后台任务,一旦该方法返回一个bitmap,我们需要立即显示在Imageview中。 imageView.setImageBitmap(bitmap) 这行代码必须在主线程执行,否则会crash。 以上三行代码如果写到一起将会导致程序卡死或者是闪退,这都取决于合理的选择线程。接下来我们看一下使用kotlin的Coroutines是如何解决这个问题的: val job = launch(Background) { val uri = Uri.withAppendedPath(imagesBaseUri,imageId.toString()) val bitmap = MediaStore.Images.Media.getBitmap(contentResolver,launch(UI) { imageView.setImageBitmap(bitmap) } } 这里最重要的是launch()和参数Background和UI,launch()表示创建和启动一个Coroutine,Background参数CoroutineContext用来保证在后台线程执行,从而保证应用程序不会卡死或者闪退,你可以声明一个如下面所示的CoroutineContext。 internal val Background = newFixedThreadPoolContext(2,"bg") 这将创建一个新的上下文,并在执行他的任务的时候使用两个常规的线程。 接下来说launch(UI),这将触发另一个coroutine,将执行在Android 可取消 接下来的挑战是处理跟Activity声明周期相关的东西,当你在加载一个任务,还没有执行完的时候离开了该Activity,以至于他在调用 job.cancel() 这就像你使用Rxjava时调用dispose和使用AsyncTask时调用cancel函数是一个道理。 LifecycleObserver Android Architecture Components 给安卓开发者提供了特别多强大的库,其中之一就是Lifecycle API.给我们提供了一个简便的方法用来实时的监听activity和fragment的生命周期,我们定义一下代码与coroutines一起使用。 class CoroutineLifecycleListener(val deferred: Deferred<*>) : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun cancelCoroutine() { if (!deferred.isCancelled) { deferred.cancel() } } } 我们创建一个LifecycleOwner的扩展函数: fun <T> LifecycleOwner.load(loader: () -> T): Deferred<T> { val deferred = async(context = Background,start = CoroutineStart.LAZY) { loader() } lifecycle.addObserver(CoroutineLifecycleListener(deferred)) return deferred } 这个方法中有太多新的东西,接下来一一解释: 现在我们可以在一个activity或fragment中调用 load方法需要一个loader作为参数,返回一个通用类型T,在load方法中我,我们调用了另外一个Coroutine的创造者async()函数,将会使用Background coroutine上下文在后台线程中执行任务,注意这个方法还有另外一个参数start = CoroutineStart.LAZY,这意味着coroutine不会立即执行,知道被调用为止。 coroutine接着会返回一个 接下来我们定义另外一个扩展函数 infix fun <T> Deferred<T>.then(block: (T) -> Unit): Job { return launch(context = UI) { block(this@then.await()) } } 这个函数将使用 这里是coroutine变得如此令人印象深刻的地方。
Kotlin Coroutine DSL 现在我们得到了两个扩展函数和一个会处理coroutine被取消的类,让我们来看看如何使用: load { loadBitmapFromMediaStore(imageId,imagesBaseUri) } then { imageView.setImageBitmap(it) } 上面的代码中,我们将lambda方法传给load函数,该函数调用loadBitmapFromMediaStore方法,该函数必须在后台线程上执行,直到该方法返回一个Bitmap,load方法的返回值是 作为扩展函数, 上面的代码可以用于任何需要在后台线程上发生的异步调用,以及返回值应该返回到主线程的地方,就像上面的例子。它不像RxJava那样可以编写多个调用,但它更容易阅读,可能会涵盖很多最常见的情况。现在你可以安全地做这样的事情,而不必担心在每个调用中造成context泄漏或处理线程; load { restApi.fetchData(query) } then { adapter.display(it) } then()和load()方法只不过是这个新库的冰山一角,但是我确实希望在未来的基于Kotlin的Android库中出现类似的东西,一旦coroutine版本达到稳定版本。在此之前,您可以使用或修改上面的代码,或者查看Anko Coroutines。我还在GitHub上发布了一个更完整的版本。 (https://github.com/ErikHellman/KotlinAsyncWithCoroutines (本地下载)). 总结 以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对编程小技巧的支持。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |