什么是Retrofit
什么是Retrofit
什么是Retrofit。官方的解释是,A type-safe HTTP client for Android and Java。一个适用于Android和java的类型安全的http客户端。事实上,Retrofit是对okHttp接口的封装,它的传输功能都是由okHttp所实现的。Retrofit利用注解的形式获得参数,并组装成uri传递给okHttp,将okHttp返回的结果进行解析,传递给用户。
Retrofit的组成
retrofit作为对okHttp接口的封装,其本身代码量并不大,但是代码结构却很精美。retrofit有几个比较重要的类。
Retrofit:定义生成了Retroift的各项参数,包括类转换器,callAdapter等,同时Retroft的create方法能生成返回接口代理实体,实现接口所要求的功能。
Call:向服务器发送一个请求,并返回响应。
CallAdapter:对Call进行适配
Converter:将http返回的数据转换为我们所需要的对象
ServiceMethod:解析注释,并且获取参数,组成url
retrofit实例
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
uerService repo = retrofit.create(uerService.class);
Call<List<Contributor>> call = repo.contributorsByAddConverterGetCall("square", "retrofit");
call.enqueue(new Callback<List<Contributor>>() {
@Override
public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
List<Contributor> contributorList= response.body();
for(Contributor contributor : contributorList) {
Log.d("login", contributor.getLogin());
Log.d("contributions", contributor.getContributions() + "");
}
}
@Override
public void onFailure(Call<List<Contributor>> call, Throwable t) {
}
});
}
}
UserService
public interface uerService {
@GET("repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributorsByAddConverterGetCall(@Path("owner") String owner, @Path("repo") String repo);
}
MainActivty中的代码结构很简单。我们可以一步一步走下来。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
这行代码定义生成了retrofit实例。baseUrl是域名,在接下来,我们可以获取接口中的注解和传入的参数,生成一个相对地址,然后将域名和相对地址结合起来,组成一个uri,发送给okhttp进行网络传输。这里我们使用了GsonConverter,他将传输过来的json数据转换成我们所需要的对象。
uerService repo = retrofit.create(uerService.class);
这行代码是retrofit的核心,create方法利用userService接口,然后返回一个uerService接口,这不是很奇怪吗?但是事实上,create方法返回的不单是一个接口,而是一个代理类。我们看一下create的源码。
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, 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);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
显然上面使用了动态代理,create返回了接口的动态代理实例。什么是动态代理实例,简单来说,就是我们在代码运行的时候的时候传入一个接口,将接口封装成一个代理类,通过这个代理类得到的constructor函数生成动态代理实体。生成代理类实体有一个参数InvocationHandler,这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
简而言之,invoke是在调用代理类的方法的时候调用的。在这里就是在下一行代码时会调用。
Call<List<Contributor>> call = repo.contributorsByAddConverterGetCall("square", "retrofit");
程序生成了serivceMethod,serivceMethod用于对注释进行了解析生成了uri,方法最后返回了一个Call。
简而言之,在enqueue中,调用了delegate.enqueue方法。这里的delegate又是什么呢?追溯源码,我们可以知道这是在create中传入的OkHttpCall实例。OkHttpCall中所实现的enqueue方法封装调用了okhttp3.call方法,并在call的callback回调函数中调用了我们传入的callback回调函数,从而在传输正确和错误时,实现我们想要的操作。
return serviceMethod.callAdapter.adapt(okHttpCall);
在这里,retrofit默认使用的事Android平台的callAdapter和E
xecutor。所以我们查看Platform中Android平台中CallAdapterFactory生成CallAdaterFactor的代码。
public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public <R> Call<R> adapt(Call<R> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
可以看到
return serviceMethod.callAdapter.adapt(okHttpCall);
中的adapt调用的就是上段代码中的adapt函数。adapt中返回一个 ExectorCallbackCall实体。
call.enqueue(new Callback<List<Contributor>>() {
@Override
public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
List<Contributor> contributorList= response.body();
for(Contributor contributor : contributorList) {
Log.d("login", contributor.getLogin());
Log.d("contributions", contributor.getContributions() + "");
}
}
@Override
public void onFailure(Call<List<Contributor>> call, Throwable t) {
}
});
这里的call事一个
ExectorCallbackCall实体,因此enqueue调用的就是ExectorCallbackCall的enqueue方法。我们看一下enqueue的源码:
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> 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<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
简而言之,在enqueue中,调用了delegate.enqueue方法。这里的delegate又是什么呢?追溯源码,我们可以知道这是在create中传入的OkHttpCall实例。OkHttpCall中所实现的enqueue方法封装调用了okhttp3.call方法,并在call的callback回调函数中调用了我们传入的callback回调函数,从而在传输正确和错误时,实现我们想要的操作。
retrofit对注解的解析
retrofit利用注解来传递参数的方式在我看来是很惊艳的。注解作为程序的元数据嵌入到程序,注解可以被解析工具或编译工具解析,我们可以将它理解为它是它所依附的类或者方法的一个内在属性。通过反射机制,我们在运行时通过字节码的方法可以获得注解信息,通过正则表达式对注解进行解析,并将传入的具体的参数来替换注解的占位符,从而获的完善的相对位置。
这些功能都是在serviceMethod中实现的。在serivceMethod类中的700多行代码中,关于注解的类型解析的代码就占了一半,从而使得retrofit能够实现各种的传递形式。
关于注解的详细介绍可以查看文档。