SmileWeather之网络篇(Retrofit+LiveData)
前言
本系列文章将以SmileWeather为实例来记录一下如何开发一款简单的APP,麻雀虽小五脏俱全。项目地址
说两句
本项目的网络请求模块是采用的Retrofit+LiveData来实现的,第一版是采用的RxJava+Retrofit+LiveData,后面发现RxJava做的事情LiveData也可以来实现,干脆就直接把RxJava剔除了。
使用
- 先添加对应的依赖,主要是retrofit的
//retrofit
api "com.squareup.retrofit2:retrofit:2.6.2"
api 'com.squareup.retrofit2:converter-gson:2.5.0'
//okhttp提供的请求日志拦截器
api 'com.squareup.okhttp3:logging-interceptor:3.14.4'
- 由于我们是采用的LiveData来承载解析后的数据,所以我们需要自定义adapter来与retrofit适配。
编写LiveDataCallAdapterFactory类,这个类的主要作用是对网络请求后解析出来的数据进行判断,是否符合我们预期的数据。
class LiveDataCallAdapterFactory :CallAdapter.Factory() {
override fun get(
returnType: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): CallAdapter<*, *>? {
if (getRawType(returnType) !=LiveData::class.java)
return null
//获取第一个泛型类型的数据
val observableType = getParameterUpperBound(0, returnType as ParameterizedType)
//获取泛型的class
val rawType = getRawType(observableType)
//判断是类型一致
if (rawType != BaseResult::class.java) {
throw IllegalArgumentException("type must be ApiResponse")
}
if (observableType !is ParameterizedType) {
throw IllegalArgumentException("resource must be parameterized")
}
return LiveDataCallAdapter<Any>(observableType)
}
}
然后编写LiveDataCallAdapter,这个主要是对数据类型进行分发。
class LiveDataCallAdapter<T>(private val responseType: Type) : CallAdapter<T, LiveData<T>> {
override fun adapt(call: Call<T>): LiveData<T> {
return object : LiveData<T>() {
private val started = AtomicBoolean(false)
override fun onActive() {
super.onActive()
if (started.compareAndSet(false, true)) {//确保执行一次
call.enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>, t: Throwable) {
//失败了就给一个错误的数据
val value = BaseResult<T>("500","500", "", "",null,null,null,null) as T
postValue(value)
}
override fun onResponse(call: Call<T>, response: Response<T>) {
//成功了,将数据发送出去
postValue(response.body())
}
})
}
}
}
}
override fun responseType() = responseType
}
- BaseResult这个实体基类一般是根据后台返回的内容格式进行创建,泛型里面的内容才是我们最终需要的数据。
data class BaseResult<T>(
var status:String,
var code: String,
var updateTime: String,
var fxLink: String,
var location: T?,
var now: T?,
var daily:T?,
var hourly:T?
)
- 接下来就是要对retrofit进行封装,由于我们采用了依赖注入的方式(hilt)所以可能和平时的有点不太一样,但是基本上都是和之前使用方式无差。依赖注入也会单独一篇记录。
@Provides
@Singleton
fun providerOkHttpClient(): OkHttpClient {
val builder = OkHttpClient.Builder()
builder.addInterceptor(initLogInterceptor())
return builder.build()
}
在这里我们可以对okhttp设置我们需要的一些东西,比如说超时时间,网络拦截器等,我这里就只加了一个日志拦截器,供开发过程中查看网络请求的详细内容。
然后对Retrofit进行初始化,添加解析器和LiveData的适配器。
@Provides
@Singleton
fun providerRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(Api.CITY_BASE_URL2)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(LiveDataCallAdapterFactory())
.client(okHttpClient)
.build()
}
- 最后差不多就是编写我们的Api接口了
/**
* 获取实时天气
*/
@GET("weather/now")
fun getNoWData(@QueryMap map: Map<String, String>): LiveData<BaseResult<NowEntity>>
- 对了还要写一个给外部提供API接口的方法(依赖注入的形式)
@Provides
@Singleton
fun providerApiService2(@Named("retrofit2") retrofit: Retrofit): ApiService2 {
return retrofit.create(ApiService2::class.java)
}
使用起来就很简单了,因为我们的数据都是通过Repository去获取的,针对不同的类型数据去不同Repository获取,下面以当前实况天气为例:
class WeatherRepository @Inject constructor(private val weatherDao: CityWeatherDao,
private val apiService: ApiService2
):Repository{
fun getNowWeatherInfo(map: Map<String, String>):LiveData<BaseResult<NowEntity>>{
return apiService.getNoWData(map)
}
}
class WeatherViewModel @ViewModelInject constructor(private val weatherRepository: WeatherRepository) :BaseViewModel(){
fun getWeatherNowInfo(id:String):LiveData<BaseResult<NowEntity>>{
return weatherRepository.getNowWeatherInfo(getParams(id))
}
}
构造方法里面的参数都交给依赖注入框架给我们提供,WeatherViewModel负责去从WeatherRepository里面拿数据然后提供给UI层。以上差不多就是这个项目里面网络这一块的简单记录了。下一篇记录一下Databinding吧