Kotlin协程和RxJava在不同业务场景下的使用体验 虽然协程和RxJava有着不同的设计理念,但他们都不约而同的解决了Java编程中回调地狱的硬伤。这篇文章就带大家尝试在特定业务场景下分别用Kotlin协程和用RxJava,来体验一把两者在代码风格上的差异。
场景一:请求数据到UI线程渲染 在Android开发中,由于主线程不能耗时请求,子线程不能更新UI,所以这是一个很常见的业务需求。
RxJava 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 fun main () { getUser().observeOn(AndroidSchedulers.mainThread()) .subscribe(Consumer { s -> println("I get RESULT $s ,CurrentThread is " + Thread.currentThread().name + "..." ) updateUI(s) }, { err -> println(err.message) }) Thread.sleep(3000 ) } fun getUser () : Observable<String> { val random = Random() return Observable .create { emitter: ObservableEmitter<String> -> println("I'm doing network,CurrentThread is " + Thread.currentThread().name + "..." ) Thread.sleep(1000 ) if (random.nextBoolean()) { emitter.onNext("Jack" ) } else { emitter.onError(TimeoutException("Net Error!" )) } } .subscribeOn(Schedulers.io()) }
Kotlin协程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 fun main () = runBlocking { try { val s = getUser() println("I get RESULT $s ,CurrentThread is " + Thread.currentThread().name + "..." ) updateUI(s) }catch (e:Exception){ println(e.message) } } suspend fun getUser () :String{ val random = Random() var res:String? = null withContext(Dispatchers.IO){ println("I'm doing network,CurrentThread is " + Thread.currentThread().name + "..." ) delay(1000 ) if (!random.nextBoolean()) { throw TimeoutException("Net Error" ) } res = "Jack" } return res!! }
跟着下面👇的代码中,将延用这里的getUser方法。
场景二:串行两次请求,后更新UI getUser -> getArticle(user)
RxJava 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 fun main () { getUser() .flatMap { user -> getArticle(user)} .observeOn(AndroidSchedulers.mainThread()) .subscribe({ article -> println("I get RESULT $article ,CurrentThread is " + Thread.currentThread().name + "..." ) }, { err -> println(err.message) }) Thread.sleep(3000 ) } fun getUser () {...}fun getArticle (user:String ) :Observable<String>{ return Observable.create { article -> println("I'm doing network[getArticle],CurrentThread is " + Thread.currentThread().name + "..." ) Thread.sleep(1000 ) article.onNext("$user 's Article~" ) } }
Kotlin协程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 fun main () = runBlocking { try { val user = getUser() val article = getArticle(user) println("I get RESULT $article ,CurrentThread is " + Thread.currentThread().name + "..." ) updateUI(s) }catch (e:Exception){ println(e.message) } } suspend fun getUser () {...}suspend fun getArticle (user:String ) :String{ val article:String withContext(Dispatchers.IO){ println("I'm doing network[getArticle],CurrentThread is " + Thread.currentThread().name + "..." ) delay(1000 ) article = "${user} 's Article" } return article }
可以看到两者都没有因此增加多少复杂度。
下面代码的网络请求操作都类似,所以会省略getXX方法。
场景三:并行两次请求,合并后更新UI RxJava 1 2 3 4 5 6 7 8 9 10 11 12 13 fun main () { RxJavaPlugins.setErrorHandler { err -> println(err.message) } getUser() .zipWith(getAnotherUser(), { user, auser -> "$user and $auser " }) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ s -> println("I get RESULT $s ,CurrentThread is " + Thread.currentThread().name + "..." ) }, { err -> println(err.message) }) Thread.sleep(3000 ) }
Kotlin协程 1 2 3 4 5 6 7 8 9 10 fun main () = runBlocking(handler) { try { val user = async { getUser() } val auser = async { getAnotherUser() } val merge = "${user.await()} and ${auser.await()} " println("I get RESULT ${merge} ,CurrentThread is " + Thread.currentThread().name + "..." ) }catch (e:Exception){ println(e.message) } }
如果getUser
耗时1s,getAnatherUser
耗时2s,最终都是耗时两秒。两边写法都还是很简洁的。
其实这些也不是RxJava真正发力的场景,比如说这时候来个场景加载九张图片,奇数张模糊化,偶数张圆角话,这时候Kotlin协程就无能为力了,因为它只是个异步工具,借助Kotlin的语言优势,处理异步问题如鱼得水。RxJava更多的体现是一种编程思维,你只需要去管做什么,不需要管怎么做。两者的设计理念还是大有不同的。