最近在新開的項(xiàng)目中,全面使用了Android的全新姿勢——Kotlin、Jetpack等等盗胀,之后也對協(xié)程進(jìn)行了一段時(shí)間的學(xué)習(xí),體驗(yàn)了協(xié)程帶來的編程快樂锄贼,我也忍不住對RxJava下了狠手。新項(xiàng)目中女阀,我完全移除了RxJava宅荤,徹底的轉(zhuǎn)入了協(xié)程時(shí)代。上周也是對原本的網(wǎng)絡(luò)層進(jìn)行了一次全新的封裝浸策,在這里對此進(jìn)行一次總結(jié)冯键。
和Google官宣的一樣,Kotlin作為官方語言將會(huì)在新特性上得到優(yōu)先支持庸汗,Retrofit2.6.0發(fā)布更新后惫确,內(nèi)置了對Kotlin Coroutines的支持,極大的簡化了使用Retrofit和協(xié)程進(jìn)行網(wǎng)絡(luò)請求的過程蚯舱。在這里我就不過多的去介紹協(xié)程的概念了改化,我們試著去使用Retrofit+協(xié)程來完成一次網(wǎng)絡(luò)請求封裝吧。
本篇基于Retrofit2.6.0進(jìn)行枉昏,與之前版本的請求方式略有差別陈肛,請注意。
Demo地址:https://github.com/jotyy/coroutines-retrofit-example 歡迎交流和star兄裂,謝謝
一句旱、創(chuàng)建Retrofit
第一步,我們當(dāng)然是要?jiǎng)?chuàng)建Retrofit晰奖,這里先封裝一個(gè)BaseRetrofitClient
.
abstract class BaseRetrofitClient {
companion object {
private const val TIME_OUT = 6
}
private val client: OkHttpClient
get() {
val builder = OkHttpClient.Builder()
val logging = HttpLoggingInterceptor()
if (BuildConfig.DEBUG) {
logging.level = HttpLoggingInterceptor.Level.BODY
} else {
logging.level = HttpLoggingInterceptor.Level.BASIC
}
builder.addInterceptor(logging)
.connectTimeout(TIME_OUT.toLong(), TimeUnit.SECONDS)
handleBuilder(builder)
return builder.build()
}
protected abstract fun handleBuilder(builder: OkHttpClient.Builder)
fun <S> getService(clazz: Class<S>, baseUrl: String): S {
return Retrofit.Builder()
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(baseUrl)
.build().create(clazz)
}
}
之后我們可以針對自己的項(xiàng)目需要去重寫handleBuilder方法,來創(chuàng)建我們需要的retrofit.
object MyRetrofitClient : BaseRetrofitClient() {
val service by lazy {
getService(MyApiService::class.java, MyApiService.BASE_URL)
}
override fun handleBuilder(builder: OkHttpClient.Builder){
...
}
}
二谈撒、創(chuàng)建Service接口
interface MyApiService {
@Post("/users")
suspend fun getUsers(): MyResponse<User>
}
三、返回?cái)?shù)據(jù)類封裝
假定現(xiàn)在服務(wù)器返回接口數(shù)據(jù)的格式為:
{
success: true
returnCode: 10000,
message: "請求成功",
returnTime: 15000901291001,
data: {
...
}
}
其中data
是我們想要獲取的實(shí)體類數(shù)據(jù)匾南,這時(shí)我們需要對外層的json進(jìn)行一次封裝啃匿,并根據(jù)returnCode處理不同的數(shù)據(jù)請求情況。
data class MyResponse<T> (
val success: Boolean,
val returnCode: Int,
val message: String,
val returnTime: Long,
val data: T
)
四午衰、統(tǒng)一處理請求
依照我們公司的內(nèi)部約定立宜,returnCode為10000時(shí)是正常的成功請求冒萄,如果請求是其它值則分別有對應(yīng)的處理,例如:80000代表token失效橙数。那么我們就想到尊流,要對不同的情況進(jìn)行統(tǒng)一處理,以此來避免在每次網(wǎng)絡(luò)請求的時(shí)候?qū)eturnCode進(jìn)行重復(fù)判斷灯帮。
4.1 BaseRepository
open class BaseRepository {
suspend fun <T: Any> apiCall(call: suspend() -> MyResponse<T>) : MyRespnse<T> {
return withContext(IO) { call.invoke() }.apply {
// 特殊處理
when (returnCode) {
99999 -> throw HttpException()
80000 -> throw TokenInvalidException()
}
}
}
class TokenInvalidException(msg : String = "token invalid"): Exception(msg)
}
4.2 BaseViewModel
open class BaseViewModel : ViewModel() {
val error by lazy { MutableLiveData<Exception>() }
// UI
fun launchUI(block: suspend CoroutineScope.() -> Unit) = viewModelScope.lauch {
try {
block()
} catch (e: Exception){
error.value = e
}
}
}
五崖技、完成一次請求
做完以上的幾件事之后,我們可以開始做一次簡單的網(wǎng)絡(luò)請求啦钟哥。
class UserRepository : BaseRepository() {
suspend fun getUser(): MyResponse<User> {
return apiCall(MyRetrofitClient.service.getUser())
}
}
直接在UserViewModel中進(jìn)行迎献。
class UserViewModel : BaseViewModel() {
val user by lazy { MutableLiveData<User>() }
val repository = UserRepository()
fun getUser(){
launchUI{
val result = repository.getUser()
user.value = result
}
}
}