注意:Coroutine和retrofit的使用在retrofit2.6之前之后是有区别的
/**
* 可以从如下网址查找测试api
* https://www.wanandroid.com/blog/show/2
*/
interface NewService {
/**
* 首页的 banner 的请求
* 2.6.0以前不需要suspend,返回值是Deferred
*/
@GET("/banner/json")
suspend fun getBanner(): Response<BannerResponse>
}
val retrofit = Retrofit.Builder()
.baseUrl("https://www.wanandroid.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())//这里和普通retrofit用法有区别,注意一下
.build()
val service = retrofit.create(NewService::class.java)
另外把依赖贴出来
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0'//LifecycleScope
val mainScope = MainScope()
override fun onDestroy() {
super.onDestroy()
mainScope.cancel()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val retrofit = Retrofit.Builder()
.baseUrl("https://www.wanandroid.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())//这里和普通retrofit用法有区别,注意一下
.build()
val service = retrofit.create(NewService::class.java)
mainScope.launch(/**MyDispatcher()*/) {
// 2.6.0以前需要在最后调用await()函数。2.6.0以后就不需要了
//retrofit运行getBanner的时候会自动在子线程,所以MyDispatcher可以不用
val data = service.getBanner()
withContext(mainScope.coroutineContext) {
if (isFinishing) {
}
}
}
}
注意一下mainScope和MyDispatcher
mainScope是Coroutine默认的用来处理UI的scope,类似于我们常说的“主线程”。具体用法就是先new一个出来,然后在页面destroy的时候调用cancel来释放所有未完成的job
MyDispatcher是用来解决线程池共用的问题。如果要用到项目中去,那么必然会涉及到和原来的retrofit共用同一个线程池。Dispatchers.Default里面分为DefaultScheduler和CommonPool。我就仿照这两个自己写了一个MyDispatcher,里面包了一层Executors.newFixedThreadPool
class MyDispatcher : CoroutineDispatcher() {
private var pool = Executors.newFixedThreadPool(10)//这里可以考虑和以前老的retrofit共用同一个线程池
override fun dispatch(context: CoroutineContext, block: Runnable) {
try {
pool.execute(block)//模仿CommonPool的实现方法
} catch (e: Exception) {
}
}
}
当然,如果不用MyDispatcher也可以用ioScope,这个是仿照mainScope来写的
val ioScope = SupervisorJob() + Dispatchers.IO
首先我们要明确,Coroutine本来是准备来取代Rxjava的。毕竟Rxjava有两个缺点,第一个学习成本很大,很多人学了两三年也仅仅只是会熟练运用而已(包括我。。。),一旦出了问题,很多时候就是无从下手(从学习成本角度来讲Coroutine简单多了)。第二个线程切换代价大。主要区别如下 (1)协程切换完全在用户空间进行,线程切换涉及特权模式切换,需要在内核空间完成; (2)协程切换相比线程切换做的事情更少。 具体可以看 为什么协程切换的代价比线程切换低? 现在的问题就出在这里,retrofit内部会自动去异步加载的,相当于又是开了一个线程。最终还是会回到线程切换,何必呢?个人觉得,协程更适合于那种不需要开子线程,同时又相当耗时的操作,比如循环遍历,文件操作,频繁的IO操作。