Retrofit的简单使用
1. 添加依赖
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
2. 创建一个interface作为Web Service的请求集合,在上面用注解写入需要配置的请求方法
interface GithubService {
@GET("/users/{user}/repos")
fun listRepos(@Path("user") user: String): Call>
}
3. 获取Retrofit实例(单例对象)
object Net {
fun instance() =
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://api.github.com/")
.build()
}
4. 创建出Service interface实例,调用对应的接口方法,创建出相应的可以用来发起网络请求的Call对象
val githubService = Net.instance().create(GithubService::class.java)
val listRepos = githubService.listRepos("hsicen")
5. 使用Call.execute()或者Call.enqueue()发起请求
listRepos.enqueue(object : Callback> {
override fun onFailure(call: Call>, t: Throwable) {
tv_content.text = t.message
}
override fun onResponse(call: Call>, response: Response>) {
tv_content.text = response.body().toString()
}
})
Retrofit源码分析
首先我们从离我们最近的位置作为切入点(通常是业务代码的最后一行),这里我们点进Call.enqueue(),看看里面的代码逻辑
/**
* Asynchronously send the request and notify {@code callback} of its response or if an error
* occurred talking to the server, creating the request, or processing the response.
*/
void enqueue(Callback callback);
发现这是一个接口,没有具体实现,切入失败;现在需要逐步回退寻找到下一个切入点Retrofit.create()
public T create(final Class service) {
Utils.validateServiceInterface(service); //接口验证
if (validateEagerly) {eagerlyValidateMethods(service);} //提前验证所有方法的正确性,会用到反射
//动态代理 创建一个运行时的类,Interface的每一个方法调用都会交由InvocationHandler来处理
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) {//继承自Object的方法
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) { //接口默认方法 Java8
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//关键代码
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
可以看到前面两行是代码健壮性验证,核心代码是Proxy.newProxyInstance()
方法来创建Service实例。这个方法会为参数中的Interface创建一个对象,这个对象实现了Interface的每个方法,并且每个方法的实现都会雷同的:调用对象实例内部的一个InvocationHandler
成员变量的invoke方法,并把自己的方法信息传递进去。这样就在实质上实现了代理逻辑;Interface中的方法全部由一个另外设定的InvocationHandler对象来进行代理操作。并且这些方法的具体实现是在运行时生成Interface实例时才确定的,而不是在编译时。这就是动态代理机制,大致像下面这样:
//GithubService.kt
interface GithubService {
@GET("/users/{user}/repos")
fun listRepos(@Path("user") user: String): Call>
@GET("/users/hsicen")
fun getUser(): Call
}
//realService
class RealService : GithubService {
private val invocationHandler = object : InvocationHandler {
private val platform = Platform.get();
override fun invoke(proxy: Any?, method: Method?, args: Array?): Any {
//扮演代理角色,对不同的方法做不同的处理
return Any()
}
}
override fun listRepos(user: String): Call> {
val method = GithubService::class.java.getMethod("listRepos")
return invocationHandler.invoke(this, method, null) as Call>
}
override fun getUser(): Call {
val method = GithubService::class.java.getMethod("getUser")
return invocationHandler.invoke(this, method, null) as Call
}
}
接下主要关注loadServiceMethod(method).invoke(args != null ? args : emptyArgs)
这句代码,loadServiceMethod()
和invoke()
是这个方法中关键作用的代码;invoke()
就是Retrofit创建Service实例的关键
abstract @Nullable T invoke(Object[] args);
点进去发现,这是ServiceMethod的抽象方法;那么只好看看loadServiceMethod方法的具体逻辑
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.parseAnnotations()
方法
static ServiceMethod 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);
}
在这个方法中主要看HttpServiceMethod.parseAnnotations()
,这个方法的代码就比较多了,我们主要看返回处的代码
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod) new SuspendForResponse<>(requestFactory,callFactory, responseConverter, (CallAdapter>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod) new SuspendForBody<>(requestFactory,callFactory, responseConverter, (CallAdapter>) callAdapter,continuationBodyNullable);
}
HttpServiceMethod
是 ServiceMethod
的子类,而HttpServiceMethod.parseAnnotations()
是返回ServiceMethod对象,然后再执行ServiceMethod的invoke方法,这里我们可以直接查看HttpServiceMethod的invoke方法
@Override final @Nullable ReturnT invoke(Object[] args) {
Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
在invoke()中创建了一个OkHttpCall,然后调用了adapt()方法,返回我们需要的对象,那么我们就来看这个adapt()方法干了什么,继续点进去
protected abstract @Nullable ReturnT adapt(Call call, Object[] args);
又是一个抽象方法,看来直接查看adapt()方法行不通,那么就只好看看OkHttpCall做了什么了,我们点进OkHttpCall看一下吧
final class OkHttpCall implements Call {
.......
}
OkHttpCall是继承自Retrofit的Call的,也就是我们第一步没走通的那个Call(因为Call的enqueue方法是抽象的),那么它有没有实现Call的enqueue()方法呢?
@Override public void enqueue(final Callback callback) {
okhttp3.Call call;
synchronized (this) {
call = rawCall;
if (call == null && failure == null) {
call = rawCall = createRawCall();
}
}
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response response = parseResponse(rawResponse);
callback.onResponse(OkHttpCall.this, response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
});
}
由于enqueue()中代码量较大,我精简了这部分代码,enqueue()主要的工作是创建了一个okhttp3的Call,然后调用这个call的enqueue()方法去发起网络请求,然后将回调结果预处理之后,交由Retrofit的Callback
因此,到这里我们对Retrofit的工作流程就有了一个大致的了解,Retrofit通过动态代理创建出Service实例,然后通过这个实例调用对应的api得到一个Retrofit的Call对象,这个Call对象又创建了一个OkHttp3的Call实例去发起网络请求,然后将结果回调给Retrofit的Callback
在上面的分析过程中,有一个点还没有解决,那就是我们在创建出OkHttpCall对象后,然后调用了adapt()方法,这个adapt()方法是干什么用的呢?做了哪些处理?
在工作中我们知道,adapter是适配器的意思,作为中间桥梁的作用;猜测是不是对OkHttpCall的转换处理呢?因为我们知道Retrofit可以和RxJava结合使用,是不是就是因为这个adapt()的作用呢?当然,我们还需要查看源码,才能找到答案
找到刚才那个抽象的adapt()方法,查看其实现的地方,我们会看到HttpServiceMethod内部有一个静态内部类实现了该抽象方法
static final class CallAdapted extends HttpServiceMethod {
private final CallAdapter callAdapter;
CallAdapted(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,Converter responseConverter,CallAdapter callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
@Override protected ReturnT adapt(Call call, Object[] args) {
return callAdapter.adapt(call);
}
}
在这个类中,创建了一个CallAdapter类,并将adapt()交由CallAdapter的adapt()来实现,这时候我们可以联想到在Retrofit初始化的时候,有这么一句代码 addCallAdapterFactory(RxJava2CallAdapterFactory.create())
,加上这句代码后,我们就可以将Call对象转化为Observable对象,和RxJava进行交互了,看来这个adapt()方法很可能就是将OkHttpCall进行转换的作用,为了验证我们的猜想,继续深入源码分析
这个静态内部类的callAdapter是构造方法传进来的,我们一步一步的向上找
CallAdapted -> callAdapter
HttpServiceMethod -> createCallAdapter()
Retrofit -> callAdapter()
Retrofit -> nextCallAdapter() callAdapterFactories.get()
最后我们发现CallAdapter来自callAdapterFactories变量,那么我们现在就需要找到callAdapterFactories的来源
// Make a defensive copy of the adapters and add the default Call adapter.
List callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
我们可以发现这个callAdapterFactories分了两次添加,一次是callAdapterFactories变量,另一次是和平台相关的默认CallAdapterFactories,我们先看这个和平台相关默认的CallAdapterFactories,我们这里就只看Android平台
@Override List extends CallAdapter.Factory> defaultCallAdapterFactories(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
DefaultCallAdapterFactory executorFactory = newDefaultCallAdapterFactory(callbackExecutor);
return Build.VERSION.SDK_INT >= 24
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
首先声明了一个Executor,看命名,这个Executor是用于回调的;然后创建了DefaultCallAdapterFactory(),我们点进这个类,看它的get()方法做了什么操作
@Override public @Nullable CallAdapter, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
return new CallAdapter
我只截取了核心代码,我们可以看到在get()方法中返回了一个CallAdapter,还有一点应该注意,我们找这个CallAdapter的原因是什么,是不是要弄清楚它的adapt()方法做了什么,刚开始由于CallAdapter的adapt()是抽象方法,所以我们找到了现在这里;那么,现在我们可以看到DefaultCallAdapterFactory类的get()方法返回的是一个CallAdapter,在它的adapt()方法里返回了ExecutorCallbackCall对象,还记得OkHttpCall么?当时OkHttpCall是通过adapt(call)方法传进来的,DefaultCallAdapterFactory类里又原封不动的传给了ExecutorCallbackCall类,现在ExecutorCallbackCall对象做了什么处理
static final class ExecutorCallbackCall implements Call {
final Executor callbackExecutor;
final Call delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback callback) {
delegate.enqueue(new Callback() {
@Override public void onResponse(Call call, final Response response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
}
可以看到,ExecutorCallbackCall是DefaultCallAdapterFactory的静态内部类,继承自Retrofit的Call,与OkHttpCall是同类的,它的作用是把操作切回主线程后再交给Callback
分析完这个和平台相关的默认CallAdapter,我们再来看看callAdapterFactories变量中的CallAdapter,我们点击callAdapterFactories变量,看看哪里对它进行了添加操作
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
callAdapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
fun instance(): Retrofit =
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl("https://api.github.com/")
.build()
这些就明白了,这个CallAdapter是我们初始化Retrofit时我们主动添加进去的,我们这里添加的CallAdapterFactory是和RxJava结合使用的,像下面这样
//GithubService.kt
@GET("/users/{user}/repos")
fun listRepos(@Path("user") user: String): Observable>
//发起网络请求
val repoService = Net.instance().create(GithubService::class.java)
repoService.listRepos("hsicen")
.observeOn(Schedulers.newThread())
.subscribe(object : Observer> {
override fun onComplete() {
}
override fun onSubscribe(d: Disposable?) {
}
override fun onNext(value: List?) {
}
override fun onError(e: Throwable?) {
}
})
但是Observable回调的接口太多了,在App中更推荐使用Single
//GithubService.kt
@GET("/users/{user}/repos")
fun listRepos(@Path("user") user: String): Observable>
//发起网络请求
val repoService = Net.instance().create(GithubService::class.java)
repoService.listRepos("hsicen")
.observeOn(Schedulers.newThread())
.subscribe(object : SingleObserver> {
override fun onSubscribe(d: Disposable?) {
}
override fun onSuccess(value: List?) {
}
override fun onError(e: Throwable?) {
}
})
源码分析总结
通过 Retrofit.create(Class) ⽅法创建出 Service interface 的实例,从⽽使得 Service 中配置的方法变得可用,这是 Retrofit 代码结构的核心
Retrofit.create() ⽅法内部,使⽤的是Proxy.newProxyInstance() ⽅法来创建Service实例。这个方法会为参数中的多 interface(具体到Retrofit来说,是固定传入⼀个interface)创建一个对象,这个对象实现了所有interface的每个方法,并且每个方法的实现都是雷同的:调⽤对象实例内部的一个InvocationHandler 成员变量的 invoke() ⽅法,并把⾃⼰的⽅法信息传递进去。这样就在实质上实现了代理逻辑:interface 中的⽅法全部由⼀个另外设定的InvocationHandler对象来进⾏代理操作。并且,这些⽅法的具体实现是在运行时⽣ interface实例时才确定的,⽽不是在编译时(虽然在编译时就已经可以通过代码逻辑推断出来)。这就是「动态代理理机制」的具体含义。
-
因此,invoke() ⽅法中的逻辑,就是 Retrofit 创建 Service 实例的关键。这个⽅法内有三行关键代码,共同组成了具体逻辑:
ServiceMethod 的创建
loadServiceMethod(method)
这⾏代码负责读取 interface 中原方法的信息(包括返回值类型、⽅法注解、参数类型、参数注解),并将这些信息做初步分析。实际返回的是一个 HttpServiceMethodOkHttpCall 的创建
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter)
OkHttpCall是retrofit2.Call 的子类。这行代码负责将ServiceMethod解读到的信息(主要是一个 RequestFactory 、一个 OkHttpClient 和⼀个 ResponseConverter )封装进 OkHttpCall ;而这个对象可以在需要的时候(例如它的 enqueue() ⽅法被调⽤的时候),利用RequestFactory和OkHttpClient来创建一个okhttp3.Call对象,并调⽤这个okhttp3.Call对象来进行⽹络请求的发起,然后利用ResponseConverter对结果进行预处理之后,交回给 Retrofit 的 Callbackadapt()⽅法
return callAdapter.adapt(new OkHttpCall...)
这个方法会使用一个CallAdapter对象来把OkHttpCall对象进行转换,⽣成⼀个新的对象。默认情况下,返回的是⼀个ExecutorCallbackCall,它的作用是把操作切回主线程后再交给Callback 。另外,如果有自定义的CallAdapter,这里也可以⽣成别的类型的对象,例例如 RxJava 的Observable ,来让 Retrofit 可以和 RxJava 结合使用。
Retrofit使用总结
1. 如果要直接获取网络返回的字符串,使用ResponseBody作为参数
@GET("sore")
Call getLol();
2. 一个Call只能被执行一次,如果要多次执行Call对象,可以通过clone,来clone一份call,从新调用
Call newCall = mCall.clone()
3. 固定参数查询,不需要动态添加参数,直接调用查询
@GET("/some/endpoint?fixed=query")
Call someEndpoint();
apiService.someEndpoint();
GET /some/endpoint?fixed=query HTTP/1.1
4. 动态参数查询,每次网络请求查询指定参数的内容
@GET("/some/endpoint")
Call someEndpoint( @Query("dynamic") String dynamic);
apiService.someEndpoint("query");
GET /some/endpoint?dynamic=query HTTP/1.1
5. 动态Map参数查询
@GET("/some/endpoint")
Call someEndpoint(@QueryMap Map dynamic);
apiService.someEndpoint(Collections.singletonMap("dynamic", "query"));
GET /some/endpoint?dynamic=query HTTP/1.1
6. 省略动态参数查询
@GET("/some/endpoint")
Call someEndpoint(@Query("dynamic") String dynamic);
apiService.someEndpoint(null);
GET /some/endpoint HTTP/1.1
7. 固定+动态参数查询
@GET("/some/endpoint?fixed=query")
Call someEndpoint(@Query("dynamic") String dynamic);
apiService.someEndpoint("query");
GET /some/endpoint?fixed=query&dynamic=query HTTP/1.1
8. 路径替换查询
@GET("/some/endpoint/{thing}")
Call someEndpoint( @Path("thing") String thing);
apiService.someEndpoint("bar");
GET /some/endpoint/bar HTTP/1.1
8. 固定头查询
@GET("/some/endpoint")
@Headers("Accept-Encoding: application/json")
Call someEndpoint();
apiService.someEndpoint();
GET /some/endpoint HTTP/1.1
Accept-Encoding: application/json
9. 动态头查询
@GET("/some/endpoint")
Call someEndpoint(@Header("Location") String location);
apiService.someEndpoint("Droidcon NYC 2015");
GET /some/endpoint HTTP/1.1
Location: Droidcon NYC 2015
10. 固定+动态头查询
@GET("/some/endpoint")
@Headers("Accept-Encoding: application/json")
Call someEndpoint(@Header("Location") String location);
apiService.someEndpoint("Droidcon NYC 2015");
GET /some/endpoint HTTP/1.1
Accept-Encoding: application/json
Location: Droidcon NYC 2015
11. Post请求,无Body
@POST("/some/endpoint")
Call someEndpoint();
apiService.someEndpoint();
POST /some/endpoint HTTP/1.1
Content-Length: 0
12. Post请求有Body
@POST("/some/endpoint")
Call someEndpoint(@Body SomeRequest body);
apiService.someEndpoint();
POST /some/endpoint HTTP/1.1
Content-Length: 3
Content-Type: text/html
xdadasd
13. 表单编码字段
@FormUrlEncoded
@POST("/some/endpoint")
Call someEndpoint( @Field("name1") String name1, @Field("name2") String name2);
apiService.someEndpoint("value1", "value2");
POST /some/endpoint HTTP/1.1
Content-Length: 25
Content-Type: application/x-www-form-urlencoded
name1=value1&name2=value2
14. 表单编码字段(Map)
@FormUrlEncoded
@POST("/some/endpoint")
Call someEndpoint( @FieldMap Map names);
apiService.someEndpoint(ImmutableMap.of("name1", "value1", "name2", "value2"));
POST /some/endpoint HTTP/1.1
Content-Length: 25
Content-Type: application/x-www-form-urlencoded
name1=value1&name2=value2
15. 动态Url(Dynamic URL parameter)
@GET
Call> getActivityList(@Url String url,@QueryMap Map map);
Call> call = service.getActivityList("http://115.159.198.162:3001/api/ActivitySubjects", map);