本文将给大家演示如何在安卓项目使用协程+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 {
//成功
data class Success(val result: T?) : DataResult()
//失败
data class Error(val errorCode:String?,val errorMessage: String?) : DataResult()
}
@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)
@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,
val steps: List)
@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>
}
因为要用到协程,所以返回了一个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 execute(call: suspend () -> Response): 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 parseApiResult(call: suspend () -> Response): DataResult {
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>()
val error = MutableLiveData()
/**
* 获取数据
*
* @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))
}
}
}
}
以上就是全部使用示例,详情代码请参考:项目源码