作者最近在学习kotlin,刚看到协程,又看到了flow.发现这两个东西是可以完成取代rxjava和LiveData的。然后我就来找不同,一起体验一下两种东西加在一起的不同. 当然了,如果你使用的是JAVA,就老老实实用RXJAVA+LiveData把。 协程是Android里面kotlin特有的。
是骡子是马,拉出来遛一遛,咋们来稍微对比一下这两种不同东西.
基础的概念我就不放了,大家可以先去了解一下,我这里只提一些重要的。
RXJAVA:链式编程,切换线程贼方便.实现异步操作,
存在的缺点:学习成本较高,操作符比较多,回调多,但比较灵活协程:线程中的“线程”,实现异步操作,切换线程也方便,结构式并发.
LiveData:响应式编程,跟随生命周期。
存在的缺点:无法在io线程中setvalue,而postvalue则会存在丢数据的情景Flow:冷流一对一,statteflow热流和liveData相似,但仍然可以在io线程中发送数据.有背压功能
如果你仍然在用Java开发Android请尽情进行改变吧,kotlin可太香了!学到了kotlin,那么特有的协程和Flow你不用它,是不是有点浪费了.
我们可以看到,我们常用RXJAVA中的异步操作,链式编程,切换线程,在协程中也是可以完美实现的,在写代码的时候让人方便阅读(可以往下面看).
而LiveData中最大的坑postValue可以通过stateflow弥补,那么我们使用Flow可没有什么太大的问题.
别说那么多,上代码就更直观的看到问题出现在哪里了.
我们来用RXJAVA写一段异步操作并且在Fragment中进行监听响应是什么样的一个效果.
0.Fragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val retrofitViewModel = ViewModelProvider(requireActivity()).get(retrofitViewModel::class.java)
retrofitViewModel.TextLiveData.observe(this,{
Log.d(TAG, "onCreate: $it")
})
retrofitViewModel.viewModelScope.launch {
retrofitViewModel.TextFlow.collect {
Log.d(TAG, "onCreate:我们来看一下订阅后的情况: $it")
//Add ViewMethod
}
}
}
1.RXJAVA+LiveData+viewModel
class retrofitViewModel : ViewModel() {
val TextLiveData by lazy {
MutableLiveData<Int>().also {
Observable.create(ObservableOnSubscribe<Int?> { emitter ->
emitter.onNext(1)
emitter.onComplete()
}).subscribeOn(Schedulers.io())
.subscribe(object : Observer<Int?> {
override fun onSubscribe(d: Disposable) {
println("onSubscribe=$d")
}
override fun onError(e: Throwable) {
println("onError=$e")
}
override fun onComplete() {
println("onComplete")
}
override fun onNext(value: Int?) {
//it.value = value 这样是不行的
it.postvalue(value)
}
})
}
}
}
2.协程+StateFlow
class retrofitViewModel : ViewModel() {
val TextFlow by lazy {
MutableStateFlow(0).also {
viewModelScope.launch(Dispatchers.IO) {
it.value = 1
}
}
}
我们来看一下结果
可以明显的看到,两个东西实现了同一个功能,异步设置某值使得Fragment可以监听到值的改变并且进行响应
.但是在代码量上的区可大了.而且StateFlow是肯定有初始值的
我们再来看一下在协程和Rxjava中并发中的不同
RXJAVA使用flapMap进行并发:
fun RxJavaParallel(){
val list = Arrays.asList(1,2,3)
Observable.fromIterable(list).flatMap{integer->
Log.d(TAG, "RxJavaParallel: TASK_$integer Start:"+System.currentTimeMillis())
getObservable(integer)
}.subscribe(object : Observer<String?> {
override fun onSubscribe(d: Disposable) {
println("onSubscribafgsdfgre=$d")
}
override fun onError(e: Throwable) {
}
override fun onComplete() {
}
override fun onNext(value: String?) {
//TODO("Not yet implemented")
Log.d(TAG, "onNext: 接收发送的任务:$value")
}
})
}
fun getObservable(integer: Int): Observable<String?>? {
return Observable.create(ObservableOnSubscribe<String?> { emitter ->
emitter.onNext("第" + integer + "圆球的第1个棱形任务")
Thread.sleep((1 * 1000).toLong())
emitter.onComplete()
Log.d(TAG, "RxJavaParallel: TASK_ finish:"+System.currentTimeMillis())
}).subscribeOn(Schedulers.newThread())
}
协程并发:
(结构化并发的话使用async),这里的并发只是不需要等待返回值所以直接使用launch
结构化并发
是当其中任何一个失败后, 应该取消另一个协程的计算. 这就是所谓的
fun CoroutineParallel(){
viewModelScope.launch {
launch {
Log.d(TAG, "CoroutineParallel: TASK_1 Start:"+System.currentTimeMillis())
delay(1000)
Log.d(TAG, "CoroutineParallel: TASK_1 Finish:"+System.currentTimeMillis())
}
launch {
Log.d(TAG, "CoroutineParallel: TASK_2 Start:"+System.currentTimeMillis())
delay(1000)
Log.d(TAG, "CoroutineParallel: TASK_2 Finish:"+System.currentTimeMillis())
}
}
}
结果:
可以看到的是大家干的都是一件事,延时1秒(阻塞)然后输出
,但是在概念理解和代码阅读上,协程确实有不少的优势.
Rxjava+Retrofit是我们常用的网络请求框架了…
当协程出来的时候,Retrofit也很快的支持了协程…
然后我们来看一下这两个不同之处…
构建okhttp客户端和retrofit.
fun createGithubApi(): NormalInterface {
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.BASIC
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.connectTimeout(30 * 1000, TimeUnit.MILLISECONDS)//连接超时
.readTimeout(20 * 1000, TimeUnit.MILLISECONDS)//读取超时,是否能读取到内容
.retryOnConnectionFailure(true)//重连
.build()
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(NormalInterface::class.java)
}
定义API接口,注意的是retrofit返回的是Observable.而协程是RepoSearchResponse
interface NormalInterface :BaseApi{
//这是RXJAVA
@GET("search/repositories?sort=stars")
fun searchRepos_X(
@Query("q") query: String,
@Query("page") page: Int,
@Query("per_page") itemsPerPage: Int
): Observable<RepoSearchResponse>
//这是协程
@GET("search/repositories?sort=stars")
suspend fun searchRepos(
@Query("q") query: String,
@Query("page") page: Int,
@Query("per_page") itemsPerPage: Int
): RepoSearchResponse
data class RepoSearchResponse(
@SerializedName("total_count") val total: Int = 0,
)
}
重点来了
,我们在viewModel中启用协程加入到StateFlow中
val TextFlow_Retrofit by lazy {
MutableStateFlow(RepoSearchResponse()).also {
viewModelScope.launch(Dispatchers.IO) {
it.value = retrofitUse.createGithubApi().searchRepos("Android", 0, 20)
}
}
}
另外一个重点
,viewModel中的LiveData使用Rxjava+Retrofit发送网络请求
//Rxjava+LiveData+retrofit+okhttp
val TextLiveData_Retrofit by lazy {
MutableLiveData<RepoSearchResponse>().also {
retrofitUse.createGithubApi_X().searchRepos_X("Android", 0, 20).
subscribeOn(Schedulers.io())
.subscribe(object:Observer<RepoSearchResponse>{
override fun onSubscribe(d: Disposable?) {
// TODO("Not yet implemented")
}
override fun onNext(value: RepoSearchResponse?) {
//TODO("Not yet implemented")
it.value = value
}
override fun onError(e: Throwable?) {
// TODO("Not yet implemented")
}
override fun onComplete() {
// TODO("Not yet implemented")
}
})
}
}
看代码大家应该也明白的差不多了…我发现网上还是感觉比较少这种比较出来的,虽然很基础,但是这样也让人更加的直观的发现这两种不同的情况.
总结一下:
RXJAVA+Retrofit 的网络请求确实也是好用的,RxJava中的操作符和学习难度是比较大的,而且封装起来也比较麻烦一些,但是代码层面上感觉阅读起来没有协程那么舒服.kotlin香也确实挺香的.
关于LiveData和Flow中的使用,学习之后是感觉在Kotlin中是比较适合Flow的.
好了,虽然官方是kotlin First,里面的高阶函数 ,协程,Flow都是比较基础也重要的特性,希望自己再接再厉,继续学习下去吧