首发于公众号: DSGtalk1989
这两尊大佛,基本都是结伴出现的,我们先什么都不管把依赖添加进来,考虑到可能存在的版本号问题,之后将不再出现具体的版本号。
//Retrofit
implementation "com.squareup.okhttp3:logging-interceptor:${okhttp3_version}"
implementation "com.squareup.retrofit2:retrofit:${retrofit_version}"
implementation "com.squareup.retrofit2:converter-gson:${retrofit_version}"
implementation "com.squareup.retrofit2:adapter-rxjava2:${retrofit_version}"
//RxJava
implementation "io.reactivex.rxjava2:rxjava:${rx_version}"
按照以往的习惯,我们需要一个类,来初始化Okhttp
和Retrofit
。并且加上一些我们需要的拦截,比如日志等等。这些东西放在类的初始化中会比较合适。
class RetrofitFactory {
val retrofit: Retrofit
init {
//打印请求log
val logging = HttpLoggingInterceptor()
logging.level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
val mOkHttpClient = OkHttpClient.Builder()
.addInterceptor(logging)
.addInterceptor(headerInterceptor())
.build()
retrofit = Retrofit.Builder()
.baseUrl(AppConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(mOkHttpClient)
.build()
}
/**
* 拦截头部
*/
private fun headerInterceptor(): Interceptor {
return Interceptor { chain ->
var request = chain.request()
//TODO 根据项目适配调整
request = request.newBuilder()
.addHeader("key", "value")
.build()
chain.proceed(request)
}
}
}
现在初始化完了,我们怎么去使用呢。一般在java中都会去构建一个单例来持有一个全局的Retrofit
对象。
所以需要用到kotlin的单例知识,这里涉及到的单例模式我们可以参考Kotlin的伴生对象()。
companion object {
//Double Check
val instance: RetrofitFactory by lazy {
RetrofitFactory()
}
fun <T> createService(service: Class<T>): T {
return instance.retrofit.create(service)
}
}
我们直接使用lazy委托的方式实现单例,见委托。这样我们在每一次想要获取到retrofit接口时,直接调用RetrofitFactory.createService(apiInterface)
即可。
那么既然我们还引入了Rxjava,要如何结合起来使用呢?
先自定义一个全局网络请求的Observer
,封装网络请求的各种情况,成功,失败,完成等等。
abstract class ResultObserver<T> : Observer<Response<T>> {
override fun onSubscribe(d: Disposable) {
if (!d.isDisposed) {
onRequestStart()
}
}
override fun onNext(reposnse: Response<T>) {
onRequestEnd()
if (reposnse.isSuccessful) {
try {
onSuccess(reposnse.body())
} catch (e: Exception) {
e.printStackTrace()
}
} else {
try {
onBusinessFail(reposnse.code(), reposnse.message())
} catch (e: Exception) {
e.printStackTrace()
}
}
}
override fun onError(e: Throwable) {
onRequestEnd()
try {
if (e is ConnectException
|| e is TimeoutException
|| e is NetworkErrorException
|| e is UnknownHostException
) {
onFailure(e, true)
} else {
onFailure(e, false)
}
} catch (e1: Exception) {
e1.printStackTrace()
}
}
override fun onComplete() {}
/**
* 请求开始
*/
open fun onRequestStart() {
}
/**
* 请求结束
*/
open fun onRequestEnd() {
}
/**
* 返回成功
*
* @param result
* @throws Exception
*/
@Throws(Exception::class)
abstract fun onSuccess(result: T?)
/**
* 返回失败
*
* @param e
* @param isNetWorkError 是否是网络错误
* @throws Exception
*/
@Throws(Exception::class)
abstract fun onFailure(e: Throwable, isNetWorkError: Boolean)
/**
* 业务错误
* 返回成功了,但是code错误
*
* @param t
* @throws Exception
*/
@Throws(Exception::class)
open fun onBusinessFail(code: Int, message: String) {
}
}
首先是继承Observer
,复写四个接口方法:onSubscribe``onNext``onError``onComplete
。然后加入了使用需要复写的请求结果业务方法:成功onSuccess
,失败onFailure
。以及给出了一些定制化选项:开始请求onRequestStart
,请求结束onRequestEnd
,业务错误onBusinessFail
我们在RetrofitFactory
文件中加入针对Observable
的扩展函数
fun <T> Observable<Response<T>>.executeResult(subscriber: ResultObserver<T>){
this.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber)
}
这样,我们直接通过Observable
对象来调用executeResult
方法,即可控制生命周期。
我们来写一个网络请求的例子:
-
定义接口
interface UserService { @GET("user") fun getPersonInfo(): Observable<Response<User>> }
很好理解,跟java基本一致。
-
调接口请求网络
RetrofitFactory.createService(WeatherService::class.java).getWeatherData() .executeResult(object : ResultObserver<WeatherData>() { override fun onSuccess(result: WeatherData?) { } override fun onFailure(e: Throwable, isNetWorkError: Boolean) { } })
就这两步,就这么简单。
如果希望能够再做更多的扩展,能够让我们的框架可以应对更多的可能。比如我们需要将服务器返回的结果做一些转换,将json数据转成我们需要的单个对象或者是数组,然后做一些修改继续使用。我们可以借助Rxjava
的flatMap
操作符,为此打造一个FlatMapUtil
,专门处理类型切换。
实际上接下去的更多的和Rxjava有关,我们先来看个简单的任务组合
//数据库任务
val dbTask : Observable<User> = UsersDatabase.instance.userDao().getUserById("test")
//网络任务
val netWorkTask : Observable<Response<WeatherData>>= RetrofitFactory.createService(WeatherService::class.java).getWeatherData()
//这两个任务合并起来
val zipTask = Observable.zip(dbTask, netWorkTask, BiFunction<User, Response<WeatherData>, Response<WeatherData>>{
user, response ->
response
})
我们需要做两个任务,而且必须要两个任务都做完了之后才能继续。
现在我们需要将网络中获得的东西做一些类型转换。我们活着放在第二步的网络任务中,活着放在合并任务的BiFunction
中,其实最终差别不大。
val netWorkTask =
RetrofitFactory.createService(WeatherService::class.java).getWeatherData()
.flatMap {
val user = User(it.body()!!.info, "Tom")
val successful = it.isSuccessful
val response = if(successful) retrofit2.Response.success(user) else it
ObservableSource<User> { observer ->
if(successful){
observer.onNext(user)
} else {
observer.onError(Throwable(response.code().toString(), Throwable(response.errorBody().toString())))
}
}
}
FlatMapUtil
就是针对我们需要的flatMap
做了一层封装,我们只需要去实现对象的转换,不需要考虑观察者的传递和异常处理。
/**
* Response的实体类型转换
*/
class FlatMapResponse2ResponseObject<T, R>(
private val response: Response<T>,
private val conversionCallBack: FlatConversionObjectInterface<T, R>
) : ObservableSource<Response<R>> {
override fun subscribe(observer: Observer<in Response<R>>) {
if (response.isSuccessful) {
val result = conversionCallBack.onConversion(response.body())
observer.onNext(Response.success(result))
} else {
observer.onError(Throwable(response.code().toString(), Throwable(response.errorBody().toString())))
}
}
}
interface FlatConversionObjectInterface<T, R> {
fun onConversion(t: T?): R
}
所以上面的代码可以简化成
val netWorkTask =
RetrofitFactory.createService(WeatherService::class.java).getWeatherData()
.flatMap {
FlatMapResponse2ResponseObject(it, object: FlatConversionObjectInterface<WeatherData, User>{
override fun onConversion(t: WeatherData?): User {
return User(it.body()!!.info, "Tom")
}
})
}