Retrofit
是基于OkHttp
封装的一个框架,使其更加方便使用。目前也是使用较多的网络框架,这里对其原理进行简单记录。这里需要注意的是Retrofit
框架只是对OkHttp
的二次封装,实际的网络请求依然还是用OkHttp
进行的。因为OkHttp
的网络请求只处理网络请求,对于实际开发中涉及的其余逻辑并不关心,比如线程切换,数据解析等等。另外使用Retrofit
还可以对api的接口进行良好的管理,因此在此基础上研发了Retrofit
框架。
这里简单记录一个使用例子,更多的例子可以查看官方文档和demo。
添加依赖
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
使用方式:
interface ServerApi {
@POST("s?cl=3&tn=baidutop10&fr=top1000&wd=战火中的中国驻乌克兰大使馆&rsv_idx=2&rsv_dl=fyb_n_homepage&sa=fyb_n_homepage&hisfilter=1")
@FormUrlEncoded
fun login(@Field("id") userId: String): Call<String>
@GET("s?cl=3&tn=baidutop10&fr=top1000&wd=战火中的中国驻乌克兰大使馆&rsv_idx=2&rsv_dl=fyb_n_homepage&sa=fyb_n_homepage&hisfilter=1")
fun loginObserver(): Observable<String>
}
class MainActivity : AppCompatActivity() {
private val httpUrl = "http://www.baidu.com"
private fun startRetrofitNet(){
val retrofit = Retrofit.Builder()
.baseUrl(httpUrl)
.addConverterFactory(ScalarsConverterFactory.create())//添加gson解析
.addConverterFactory(GsonConverterFactory.create())//添加字符串解析
//.addCallAdapterFactory() 添加适配器转换
.build()
val serverApi = retrofit.create(ServerApi::class.java)
val loginCall = serverApi.login("123")
// loginCall.execute()//同步方法
loginCall.enqueue(object : Callback<String>{
override fun onResponse(call: Call<String>, response: Response<String>) {
val result = response.body()
Log.e("YM--->","获取的网络内容:${result}")
Toast.makeText(this@MainActivity,"--->",Toast.LENGTH_SHORT).show()
}
override fun onFailure(call: Call<String>, t: Throwable) {
}
})
}
}
从上文的使用方式可以看到,Retrofit
的使用方式和OkHttp
有很多不同的地方:
OkHttp
那样涉及到其他部分,比如Request
。这里都可以使用Retrofit
创建的对象进行链式调用进行请求。这是一种基于装饰模式的写法(跟建造者模式不是一回事,区别在于一个是根据多个参数不一样进行动态配置)。ConverterFactory
进行数据转换。因为有时候传入的数据格式不支持,比如传入一个对象,这时候可以使用转换器转换成字符串,或者将一个字符串在返回的时候转换为一个对象。CallAdapterFactory
对OkHttp
的Call
对象进行适配。在OkHttp
中Call
是用来对Request
和Response
进行处理的。这里可以看上述接口定义了两个不同的返回值,一个是Call
,另外一个是RxJava的Observable
。使用RxJava的话就可以调用不同的特性,这里就是使用CallAdapterFactory
进行转换的。也可以转换成kotlin这些使用方式。Retrofit
通过重新处理Call
将返回值根据实现的不同的CallAdapterFactory
做不同的转换,提高了强大的拓展性。Toast
。要知道请求是子线程的,Toast
显示是在主线程的,所以这里自己做了线程切换。也是一个很大的不同点。这个线程切换是在Retrofit
默认的DefaultCallAdapterFactory
中做的,其它适配器里面没写的话是没有线程切换的。Retrofit
类里面的,所以实例对象不一样的话等于没有缓存。)。上面就是跟OkHttp
的一些区别,那么对于Retrofit
来说,主要有以下几部分组成:
Retrofit
ConverterFactory
CallAdapterFactory
Call
对于他们的主要功能上文已经解释过了,这里就不再进行解释,下面从最基本的网络请求来记录Retrofit
的运行流程。
使用Retrofit
的时候首先要通过建造者模式创建一个实例对象,最终实例对象还是调用的Retrofit
的构造函数,所以这里看下构造函数即可知道有哪些参数:
Retrofit(
okhttp3.Call.Factory callFactory,
HttpUrl baseUrl,
List<Converter.Factory> converterFactories,
List<CallAdapter.Factory> callAdapterFactories,
@Nullable Executor callbackExecutor,
boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
这里简单说下这几个参数的含义:
callFactory
: 这个是okhttp
的一个接口,OkHttpClient
就是实现了这个接口,所以可以传入自定义的OkHttpClient
baseUrl
:故名思义,就是一个网络请求的基础域名converterFactories
:数据转换器集合callAdapterFactories
: 适配器集合callbackExecutor
: 一个用来调度返回值的回调的线程池,默认是主线程validateEagerly
: 这个用的不多,源码的意思是检查配置是否有效。通过源码知道定义的接口类是通过动态代理实现具体类的,但是里面的接口需要在调用的时候才进行检查,而这个参数的配置会提前进行检查,即不通过动态代理就可以执行。当创建完Retrofit
的实例化对象后,需要通过Retrofit::create(final Class
来创建接口的实例化对象。这里看下这个对象是如何创建的。
public final class Retrofit {
...
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
...
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
...
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
...
}
这里可以看到是使用动态代理的方式创建的,如果是系统默认函数就执行默认函数,如果是自己写的,就执行ServiceMethod> loadServiceMethod(Method method)
函数。然后可以看到通过ServiceMethod.parseAnnotations(this, method);
得到结果,然后添加进缓存里面去。这里看下这个写法:
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
method,
"Method return type must not include a type variable or wildcard: %s",
returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract @Nullable T invoke(Object[] args);
}
这里拿到相关方法后进行解析注解然后得到相对应的实体,然后在动态代理里面调用loadServiceMethod(method).invoke(args);
的invoke(args)
函数就是这里面的函数。看下具体实现。
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
...
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
...
}
通过这里就知道在具体实现的适配器里面将数据返回,这个数据结构类型由具体的适配器进行构造。默认的就返回默认的Call
对象,RxJava就返回Observer
对象