本文將給大家演示如何在安卓項目使用協程+Retrofit進行網絡請求,項目使用MVVM架構,引用了架構組件ViewModel和LiveData,包含了異常的封裝處理,可以用在一個商用產品架構上。
1.引入依賴
//使用協程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"
//OkHttp
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
//retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
//retrofit協程適配器
implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2"
//Moshi(Json解析)
implementation 'com.squareup.moshi:moshi-kotlin:1.9.2'
kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.9.2'
implementation 'com.squareup.retrofit2:converter-moshi:2.6.2' //retrofit-moshi解析器
2.創建實體類
/**
* 封裝的網絡請求結果實體類
*
* @author king
* @date 2019-11-29 09:53
*/
sealed class DataResult<out T : Any> {
//成功
data class Success<out T : Any>(val result: T?) : DataResult<T>()
//失敗
data class Error(val errorCode:String?,val errorMessage: String?) : DataResult<Nothing>()
}
@JsonClass(generateAdapter = true)
data class MenuResult(val resultcode: String,
val reason: String,
val result: ResultBean,
val error_code: Int)
@JsonClass(generateAdapter = true)
data class ResultBean(val totalNum: String,
val pn: String,
val rn: String,
val data: List<FoodMenu>)
@JsonClass(generateAdapter = true)
data class FoodMenu(val id: String,
val title: String,
val tags: String,
val imtro: String,
val ingredients: String,
val burden: String,
val albums: List<String>,
val steps: List<StepsBean>)
@JsonClass(generateAdapter = true)
data class StepsBean(val img: String,
val step: String)
2.創建接口,用於請求網絡接口
interface NetworkApi {
@GET("/cook/query?key=${AppConfig.JUHE_KEY}")
fun getFoodMenuAsync(@Query("menu") menu:String,
@Query("pn") pn:Int, @Query("rn") rn:Int) : Deferred<Response<MenuResult>>
}
因爲要用到協程,所以返回了一個Deferred,而Response是Retrofit的網絡請求結果,MenuResult爲實體類
3.創建retrofit請求工具類
object ApiService {
//構建OkHttpClient
private val okHttpClient = OkHttpClient().newBuilder()
.connectTimeout(60, TimeUnit.SECONDS)
.proxy(Proxy.NO_PROXY)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build()
//構建retrofit
private fun retrofit(): Retrofit = Retrofit.Builder()
.client(okHttpClient)
.baseUrl(AppConfig.BASE_API_URL)
.addConverterFactory(MoshiConverterFactory.create())//moshi解析json適配器,自動將json解析爲對象
.addCallAdapterFactory(CoroutineCallAdapterFactory())//支持kotlin協程
.build()
//創建接口對象
val api: NetworkApi = retrofit().create(NetworkApi::class.java)
/**
* 執行網絡請求操作,並處理異常,這一方法也可忽略,在具體調用位置可以自己處理
*
* suspend聲明只能在協程內執行
*/
suspend fun <T : Any> execute(call: suspend () -> Response<T>): T? {
val data = parseApiResult(call)
var result: T? = null
when (data) {
is DataResult.Success -> result = data.result
is DataResult.Error -> {
//請求失敗
LogUtil.e(data.errorMessage)
}
}
return result
}
/**
* 執行並處理網絡請求結果(包含異常處理)
*/
private suspend fun <T : Any> parseApiResult(call: suspend () -> Response<T>): DataResult<T> {
try {
val response = call.invoke()
if (response.isSuccessful) {
return DataResult.Success(response.body())
}
return DataResult.Error(response.code().toString(), response.message())
}catch (e:Exception){
LogUtil.e(e)
return DataResult.Error("", e.message)
}
}
}
4.發起調用,並處理結果
class HomeViewModel : ViewModel() {
val menuData = MutableLiveData<List<FoodMenu>>()
val error = MutableLiveData<DataResult.Error>()
/**
* 獲取數據
*
* @param name
* @param page
* @param count
*/
fun getMenuData(menu: String, page: Int, count: Int) {
viewModelScope.launch(Dispatchers.Default) {
//使用Retrofit接口
val res = ApiService.execute(
call = { ApiService.api.getFoodMenuAsync(menu, page, count).await() })
// val result = ApiService.execute (call= {
// ApiService.api.getFoodMenuStr(menu, page, count).await()
// })
// LogUtil.i("結果$result")
// val moshi = Moshi.Builder()
// .add(KotlinJsonAdapterFactory())
// .build()
// val adapter = moshi.adapter(MenuResult::class.java)
// val res = adapter.fromJson(result)
if(res == null){
//爲空,說明請求失敗了
error.postValue(DataResult.Error("400", "Network Error"))
return@launch
}
//請求成功
if (res?.resultcode == "200") {
//接口數據正常
menuData.postValue(res?.result?.data)
} else {
error.postValue(DataResult.Error(res?.resultcode, res?.reason))
}
}
}
}
以上就是全部使用示例,詳情代碼請參考:項目源碼