我: 爲何已經有Rx了還要在這裏用協程?
某同事: 由於協程比線程小,能夠開不少個。
我:...... 爲何比線程小?html
我維基了一下,確實有說比線程更小。
可是看了一些源碼,也是線程池 + 線程實現的,這時就開始有了疑惑,爲何一樣是線程,怎麼就說是比線程小的東西呢?
直到看到了Benny大佬的文章 協程爲何被稱爲『輕量級線程』解釋,我清晰了。經過測驗,確實啓動成千上萬個協程也不會出現OOM或者其餘問題。android
implementation"com.squareup.retrofit2:retrofit:2.6.2"
implementation"com.squareup.retrofit2:converter-gson:2.6.2"
// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2'
複製代碼
記得使用retrofit 2.6.0 或以上git
interface GitApi {
@GET("/users/{username}/{module}")
suspend fun repos( @Path("username") username: String, @Path("module") module: String, @Query("page") currPage: Int ): List<RepoInfo>
@GET("/search/repositories")
suspend fun searchRepos( @Query("q") key: String, @Query("sort") sort: String? = "updated", @Query("order") order: String? = "desc", @Query("page") currPage: Int ): SearchResponse
}
複製代碼
fun buildRetrofit(): Retrofit {
builder.addInterceptor(HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
Timber.d(message)
}
}).apply {
level = HttpLoggingInterceptor.Level.BODY
}).addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val userCredentials = "$username:$password"
val basicAuth =
"Basic ${String(Base64.encode(userCredentials.toByteArray(), Base64.DEFAULT))}"
val original = chain.request()
val requestBuilder = original.newBuilder()
.header("Authorization", basicAuth.trim { it <= ' ' })
val request = requestBuilder.build()
return chain.proceed(request)
}
})
return Retrofit.Builder()
.client(builder.build())
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
複製代碼
kotlinx.coroutines.CoroutineScope
接口中的註釋有這麼一句話
github
Scope
,也就是說每一個suspend函數都有一個
CoroutineScope
,若是沒有必定會報錯。而Android的ViewModel中有擴展了一個
viewModelScope
,而且跟
lifecycle
綁定了,因此在ViewModel的onCleared方法上會自動幫咱們cancel掉這個viewModelScope的全部Jobs。
open class BaseViewModel : ViewModel() {
fun <T> request( onError: (error: Throwable) -> Unit = {}, // 不須要處理Error能夠不傳
execute: suspend CoroutineScope.() -> T
) {
viewModelScope.launch(errorHandler { onError.invoke(it) }) {
launch(Dispatchers.IO) {
execute()
}
}
}
private fun errorHandler(onError: (error: Throwable) -> Unit): CoroutineExceptionHandler {
return CoroutineExceptionHandler { _, throwable ->
Timber.d(throwable)
onError.invoke(throwable)
}
}
}
複製代碼
使用繼承 BaseViewModel
調用 request
方法便可。網絡
class RepoViewModel(
private val userRepo: UserDataSource,
private val gitApi: GitApi
) : BaseViewModel() {
private val _reposResult = BaseLiveData<List<RepoInfo>>()
val repoResult: BaseLiveData<List<RepoInfo>>
get() = _reposResult
fun fetchRepos(module: String) {
request {
userRepo.currUser()?.let {
val result = gitApi.repos(it.nickname, module, 1)
_reposResult.update(result)
}
}
}
}
複製代碼
ORapp
fun fetchRepos(module: String) {
request(
onError = {
// handle error
},
execute = {
userRepo.currUser()?.let {
val result = gitApi.repos(it.nickname, module, 1)
_reposResult.update(result)
}
}
)
}
複製代碼
剩下的基本是LiveData和Fragment之間的訂閱上的邏輯實現了。ide
最近在學習,瞭解也不是很深,歡迎評論補充和提建議。 學習項目地址 Dithub。函數