网络通信框架-Retrofit

简介:

Retrofit 是Square公司推出的一个类型安全的Http的适应于Android,java客户端的框架

下载:

Gradle: compile ‘com.squareup.retrofit2:retrofit:2.1.0’
要求 java7, Android2.3以上
官网:http://square.github.io/retrofit/

使用:

  1. 申明一个API接口

    1
    2
    3
    4
    public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>>listRepos(@Path("user") String user);
    }
  2. 使用Retrofit类实现这个接口

    1
    2
    3
    4
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();
    GitHubService service = retrofit.create(GitHubService.class);
  3. 同步或异步向服务器发送http请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Call<List<Repo>> repos = service.listRepos("octocat");
    repos.enqueue(new Callback<List<Repo>>){
    @Override
    public void onResponse(Response<List<Repo>> response,Retrofit retrofit){
    }
    @Override
    public void onFailure(Throwable t){
    }
    }

使用注解描述请求

  • 支持URL参数和请求参数
  • 请求实体对象转换
  • 多种请求实体和文件上传

代码结构

其中http包为各种注解




  • Body
    用来指定POST或PUT请求里以实体形式发送的请求方法的参数,对象将会被序列化并放在请求body中

  • DELETE
    用来指定一个删除请求

  • Field
    用来指定form-encoded请求的指定键值参数,支持多个多个字符串指定

  • FieldMap
    用来指定form-encoded请求的未指定键值对参数

  • FormUrlEncoded
    用来指定一个Url被加密,使请求带有application/x-www-form-urlencoded MIME 类型
    键值对将会由之前的UTF-8编码RFC-3986

  • GET
    用来指定一个GET请求

  • HEAD
    用来指定一个HEAD请求

  • Header
    用来指定请求头部参数,如果有重复,不会被重写,会被同时包含在头文件里

  • Headers
    用来指定头部键值对信息

  • HTTP
    用来指定HTTP形式请求

  • Multipart
    表示请求实体为多个,每部分使用@Part注解在参数中声明

  • OPTIONS
    用来指定一个OPTIOINS请求

  • Part
    用来指定含有多个部分请求的一个部分

  • PartMap
    表示键值对部分

  • PATCH
    用来指定一个PATCH请求

  • Path
    用来在参数中指定url被替换的部分

  • POST
    用来指定一个POST请求

  • PUT
    用来指定一个PUT请求

  • Query
    用来指定Url路径的查询键值对参数

  • QueryMap
    用来指定Url路径的查询键值对

  • Streaming
    用来指定Response body为字节数组形式返回

  • URL
    用来指定完整路径

原理分析

首先先构建一个Retrofit对象,从构建方式可以看出,使用的是设计模式中的Builder模式,Builder模式用于构建复杂的对象,并将构建过程和它的部件解耦,使用户在不知道内部构建细节的情况下精细的控制对象的构建流程。

最后在build方法中设置相关参数,如果未设置callFactory会构造默认的okHttpClient对象,其余的则默认构造Platform类的默认方法获取相关对象,然后将相关参数通过Retrofit类的构造方法构造出Retrofit对象

通过Retrofit类的create方法,返回一个动态代理的对象,这里涉及到涉及模式中的代理模式,也称为委托模式,相关代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}

首先调用工具类方法validateServiceInterface()验证接口,这里将会验证传进来的类是否是接口类型,如果不是则报出“API申明必须是接口”的无效参数异常,如果是接口,则继续检查该类是否实现了其它接口,如果是,则报出“API 接口不能是其它接口延伸的”的无效参数异常

1
2
3
4
5
6
7
8
9
10
11
static <T> void validateServiceInterface(Class<T> service) {
if (!service.isInterface()) {
throw new IllegalArgumentException("API declarations must be interfaces.");
}
// Prevent API interfaces from extending other interfaces. This not only avoids a bug in
// Android (http://b.android.com/58753) but it forces composition of API declarations which is
// the recommended pattern.
if (service.getInterfaces().length > 0) {
throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
}
}

然后通过eagerlyValidateMethods()方法的loadServiceMethod()方法生成serviceMethod对象并一起存入缓存map中,通过platform这个类进行平台判断,对java8 特性 default method 方法做了兼容处理,这里做了个同步锁,防止多线程下重复put ServiceMethod对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}

当执行阶段时,okHttpCall 会调用enqueue()方法,内部调用createRawCall() 方法生成okhttp3.Call对象,通过ServiceMethod实例的toRequest()方法将相关信息由RequestBuilder类构建成Request类请求并通过okHttpCall真正调用okhttp3发送请求。

1
2
3
4
5
6
7
8
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}