Retrofit的定位
Retrofit并不是作为一个网络请求库存在的,而是作为一个网络请求适配层,就比如网络请求库如果是jdbc,那么MyBatis就是Retrofit。Retrofit2底层默认使用OkHttp作为其网络请求库,而其本身只是对网络请求过程的一般操作做了封装,例如参数的构建,数据的解析以及线程切换。
Retrofit秉承Restful风格的简洁性,将每个网络请求定义为java接口的一个方法,定义路径可以由方法的注解提供,header和请求参数可以由方法的参数提供,返回一个Retrofit的Call对象,这种Call对接类似OkHttp的Call,可以有execute和enqueue两种方式执行网络请求,分别是同步和异步。
Retrofit Demo
还是使用上篇OkHttp3的demo,只不过把样式定义抽取出来作为公共函数,只把网络请求暴露出来。
package com.benson.android.network
import android.content.Context
import android.graphics.Color
import android.view.Gravity
import android.view.ViewGroup
import android.widget.*
fun initView(context: Context, search: (TextView, String) -> Unit): ViewGroup {
val content = LinearLayout(context)
content.orientation = LinearLayout.VERTICAL
val searchBar = LinearLayout(context)
searchBar.orientation = LinearLayout.HORIZONTAL
searchBar.gravity = gravity.center is For Sale
val input = EditText(context)
input.textSize = DisplayUtil.sp2px(context, 10.0F) * 1.0F
searchBar.addView(input, LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 7.0F))
val searchBtn = Button(context)
searchBtn.text = "search"
searchBtn.textSize = DisplayUtil.sp2px(context, 10.0F) * 1.0F
searchBar.addView(searchBtn, LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 2.0F))
content.addView(searchBar, LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.0F))
val result = TextView(context)
result.setBackgroundColor(color.black - 这个网站可出售。 - 最佳的color 来源和相关信息。)
result.setTextColor(Color.WHITE)
result.textSize = DisplayUtil.sp2px(context, 15.0F) * 1.0F
val scrollView = ScrollView(context)
scrollView.addView(result)
content.addView(scrollView, LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 10.0F))
searchBtn.setOnClickListener {
search(result, input.text.toString())
}
return content
}
```
这里还是用OkHttp的基本配置,并且使用异步回调
```
package com.benson.android.network
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import okhttp3.OkHttpClient
import retrofit2.*
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.Path
import java.lang.StringBuilder
import java.util.concurrent.TimeUnit
class Retrofit2DemoActivity: AppCompatActivity() {
companion object {
const val BASE_URL = "https://api.github.com/users/"
interface UserResource {
@GET("{user}")
fun getUser(@Path("user") user: String): Call<User>
}
}
val client by lazy { OkHttpClient.Builder()
.retryOnConnectionFailure(true) // 连接超时重试
.connectTimeout(2L, TimeUnit.MINUTES) // 2s 连接超时
.readTimeout(2L, TimeUnit.MINUTES) // 2s 读超时
.writeTimeout(2L, TimeUnit.MINUTES) // 2s 写超时
.build()
}
val retrofit by lazy { Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
}
val userResource by lazy {
retrofit.create(UserResource::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(initView(this) { result, user ->
userResource.getUser(user).enqueue(object: Callback<User> {
override fun onFailure(call: Call<User>, t: Throwable) {
result.text = "query error with user=$user\n${t.message}"
}
override fun onResponse(call: Call<User>, response: Response<User>) {
result.text = StringBuilder("code=${response.code()}").append("\n")
.append("message=").append(response.message()).append("\n")
.append("body=").append(response.body()).toString()
}
})
})
}
}
这里的实体类是用json转javabean自动生成的就不贴了
整个请求过程简单描述就是使用Retrofit.Builder构造出一个Retrofit对象,使用Retrofit对象create出我们需要使用的Restful接口对象。这个接口不需要我们自己来实现,Retrofit会自动帮我们来实现,请求时需要把对应的参数传给接口的方法就行,这样的网络请求就像调用了一个普通的java方法,而异步请求的回调是Retrofit帮我们自动切换到了main线程。
Retrofit源码解析
还是老套路,首先看构造过程,先是Retrofit对象的构造过程,OkHttpClient的构造过程就不再赘述了,Retrofit使用Builder,传入一系列的构造参数,最后调用build方法返回一个Retrofit对象。build方法的最后就是new了一个Retrofit对象,将callFactory、baseUrl、converterFactories、callAdapterFactories、callbackExecutor以及validateEagerly对象传入Retrofit的构造器。
- callFactory: 表示网络请求器,真正执行网络请求的接口
- baseUrl: 网络请求的url前缀,Retrofit认为同一个Retrofit表示同一个域名下的一系列请求
- converterFactories:类型转换器工厂,生产Converter,Converter负责将java对象转化为网络请求request参数,也负责把response结果转换为java对象。
- callAdapterFactories:call的适配器工厂,生产CallAdapter,CallAdapter主要负责将底层返回的Call类型转换为请求接口定义的返回类型。
- callbackExecutor,异步回调的执行器,默认使用MainThreadExecutor,执行的runnable使用Handler(Looper.getMainLooper()).post。
- validateEagerly:是否提前缓存RetrofitInterface中定义的方法到Retrofit中来
Retrofit对象使用create方法传入一个接口的Class创建出这个接口的代理对象。Retrofit默认使用了Java的Proxy来创建代理对象,代理对象的实现是创建ServiceMethod对象,并调用其adapt方法,也就是请求接口的方法被调用时,其实是调用的相应ServiceMethod的adapt方法。前面说到validateEagerly是是否提前缓存RetrofitInterface中定义的方法,create方法这里就使用了这个validateEagerly,如果validateEagerly为true,就提前将所有的ServiceMethod创建并缓存起来,而真正调用时通过loadServiceMethod获取ServiceMethod时,就可以从缓存中获取。
ServiceMethod的adapt方法直接调用了callAdapter.adapt,ServiceMethod的callAdapter是在其build的时候使用createCallAdapter方法创建出来的,而ServiceMethod的createCallAdapter方法则是获取请求接口方法的注解和return类型,使用retrofit对象的callAdapter方法创建。在Retrofit对象中,callAdapter直接调用了nextCallAdapter方法,使用callAdapterFactories来创建CallAdapter对象。这里的callAdapterFactories是build创建Retrofit对象时传入进来的,如果不传,也会add进platform.defaultCallAdapterFactory。defaultCallAdapterFactory方法中对传入参数callbackExecutor进行判空,当然,这里的callbackExecutor不为空,因为默认是在main线程中回调,因此callAdapterFactory默认使用ExecutorCallAdapterFactory。而ExecutorCallAdapterFactory的get方法中则创建了一个CallAdapter对象,其adapt方法则是返回创建的ExecutorCallbackCall对象,这个ExecutorCallback对象也就是我们在最外面使用userResource.getUser(user)返回的一个Call的真实对象。
这样真正的请求过程就落在了ExecutorCallAdapterFactory的enqueue方法里了,enqueue方法中只调用了delegate的enqueue方法,并在onReponse中调用传入的callback对象的onResponse,在onFailture中调用callback的onFailure,并且回调都被post到了callbackExecutor中,而这里的delegate对象是创建ExecutorCallback时传进来的OkHttpCall对象,在enqueue的实现中,则是使用了OkHttp的Call,调用其enqueue方法,并在enqueue方法的callback回调中,调用ExecutorCallback的相应回调,这样就完成了OkHttp的请求代理Retrofit的请求,如果要实现其他网络请求底层库来实现Retrofit的请求,也可以模仿OkHttp对Retrofit的代理实现。
由于OkHttp没有自带javabean解析,因此需要Retrofit来适配OkHttp请求结果到javabean的解析过程,在OkHttpCall的response回调中,使用了parseResponse,在其中调用了serviceMethod.toResponse创建出了javabean对象。而ServiceMethod的toResponse方法则是直接调用了responseConverter的convert方法,还记得在demo中创建Retrofit的时候convert是传的GsonConverterFactory,因此这里使用的converter的真实对象则是GsonConverterFactory的responseBodyBodyConverter返回的GsonResponseBodyConverter对象。其convert方法则是直接使用了JsonReader对象,调用read方法构造出的javabean对象。
总结
Retrofit并不是一个网络请求库,而是一个网络请求适配库,是将网络请求适配到应用开发的库,使得调用网络请求像调用一个普通的java方法一样方便,而且遵循了restful的接口风格。Retrofit对网络请求使用java的接口进行定义,接口的方法表示请求参数,请求的额外信息由注解信息表示。而这个java接口不需要我们开发来实现,而是Retrfit内部使用了java的动态代理Proxy框架实现的,在invoke方法中,使用了构造的ServiceMethod方法来完成Call的构建,而Call对象的真正执行execute和enqueue两个方法,又被代理给真正的网络请求库。
而对于网络请求参数或者网络请求结果如何与javabean进行转化的问题,Retrofit提供了Converter<F, T>接口,其只有一个方法convert,作用是把给定的类型F转化为给定义的类型T。
Retrofit现在变得越来越流行,大家都来越关心Retrofit甚至超过了OkHttp,这是因为大家的注意力在应用开发架构上,而应用开发的过程中,开发模式往往比底层实现更重要,这也是为什么Java语言对C/C++更流行的原因,Java底层全是C/C++,那么用C/C++开发不就好了吗?但问题恰恰就在于Java开发能够让开发者更方便地让注意力放在开发模式上,而不是底层实现上,正如Retrofit能够让开发者像调用普通java方法一样进行网络请求,而不用关心网络请求到底作了哪些操作。