Retrofit2使用方式和源码解析

简单介绍

Retrofit是一套RESTful架构的Android(Java)客户端实现,基于注解,提供JSON to POJO(Plain Ordinary Java Object,简单Java对象),POJO to JSON,网络请求(POST,GET,PUT,DELETE等)封装。

软件首页http://square.github.io/retrofit/

github地址https://github.com/square/retrofit

具体使用

官方文档非常清晰,Retrofit2对Retrofit做了很大改进,针对改进做一下分析和使用说明

底层okhttp不同

Retrofit 默认使用okhttp
Retrofit2 默认使用okhttp3

Service接口定义方式不同

在Retrofit2之前如果定义一个同步的函数,应该这样定义:
public interface GitHubService {
  
    @POST("/list")
    Repo loadRepo();
  
}
异步函数定义
public interface GitHubService {
  
    @POST("/list")
    void loadRepo(Callback<Repo> cb);
  
}
Retrofit2 同步异步方法定义一个接口就可以了
mport retrofit.Call;
public interface GitHubService {
  
    @POST("/list")
    Call<Repo> loadRepo();
  
}

如果要调用同步请求,只需调用execute;而发起一个异步请求则是调用enqueue。

Retrofit2 可以取消请求方法

Retrofit2请求使用Call,Call有cancel方法可以取消请求
只需调用call.cancel()就可以取消请求。

Converter现在从Retrofit2中删除,需要根据自己的需要引入Converter

这里是Square提供的官方Converter modules列表。选择一个最满足你需求的。
Gson: com.squareup.retrofit:converter-gson
Jackson: com.squareup.retrofit:converter-jackson
Moshi: com.squareup.retrofit:converter-moshi
Protobuf: com.squareup.retrofit:converter-protobuf
Wire: com.squareup.retrofit:converter-wire
Simple XML: com.squareup.retrofit:converter-simplexml

Retrofit2 新的URL定义方式

Retrofit接口请求URL要求用/开头,必须设置baseUrl,接口请求URL不能是完整路径。可以使用Endpoint切换服务器路径
于 Retrofit2中新的URL定义方式,建议
- Base URL: 总是以 /结尾
- @Url : 不要以 / 开头
Retrofit2中没有Endpoint这个类,@Url中可以包含完整的路径,包含完整路径baseUrl会被忽略
public interface APIService {
    @POST("http://api.test.com/test)
    Call<Users> loadUsers();
}

Retrofit2 需要OkHttp的支持

OkHttp在Retrofit里是可选的。如果你想让Retrofit 使用OkHttp 作为HTTP 连接接口,需要手动包含okhttp 依赖。
但是在Retrofit2中,OkHttp 是必须的,并且自动设置为了依赖。
Retrofit2缺少INTERNET权限会导致SecurityException异常
在Retrofit 中,如果忘记在AndroidManifest.xml文件中添加INTERNET权限。异步请求会直接进入failure回调方法,得到PERMISSION DENIED 错误消息。没有任何异常被抛出。但是在Retrofit2中,当调用call.enqueue或者call.execute,将立即抛出SecurityException,如果不使用try-catch会导致崩溃。

即使response存在问题onResponse依然被调用

在Retrofit中,如果获取的 response 不能背解析成定义好的对象,则会调用failure。但是在Retrofit2中,不管 response 是否能被解析。onResponse总是会被调用。但是在结果不能被解析的情况下,response.body()会返回null。只有抛出异常才会调用onFailure

拦截器不同

在Retrofit中,可以使用RequestInterceptor来拦截一个请求,但是它已经从Retrofit2 移除了,因为HTTP连接层已经完全转为OkHttp。
Retrofit2使用okhttp的拦截器


源码分析

Retrofit的创建

Retrofit是个final类,不能再定义子类,Retrofit没有public构造方法,只能使用构建的者方式Retrofit.Builder构建。
etrofit.Builder可以指定url根地址、采用的网络客户端、回调线程池、请求拦截器、返回数据格式器和错误处理。
通常必须调用baseUrl,addConverterFactory这两个方法。
构建器默认使用OkHttpClient作为网络请求工具,addCallAdapterFactory用来构建请求实例,callbackExecutor指定回调方法线程池,根据不同平台构建不同,Android构建的是defaultCallbackExecutor,默认在主线程中。validateEagerly默认为false,表示执行的时候再去解析注解。如果设置为true,那么create实例之后,就会先解析接口所有方法。

if (baseUrl == null) {
       throw new IllegalStateException("Base URL required.");
     }<pre name="code" class="java"> public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
......

请求实例的创建

调用Retrofit的create方法,创建了执行请求对象的代理,代理方法的执行中,对于Object方法和默认方法,直接执行,其它方法解析后执行。使用代理加注解,是Retrofit的核心思想。
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);
          }
        });

接口的解析过程

loadServiceMethod中首先从缓存中找解析的方法,找到直接返回,否则解析方法,然后放入缓存中。解析过的接口保存在了serviceMethodCache中,防止重复解析,提高效率。解析方法使用ServiceMethod.Builder构建解析器。
build过程中,首先创建callAdapter,
解析中有一系列规则,发现异常会抛出异常
方法返回的参数不能是泛型,不能是基本数据类型,不能是void。
注解不能为空
创建完callAdapter继续构建其他ServiceMethod属性,需要注意其他抛出异常的情况
返回类型不能是一个Response
httpMethod注解不能为空
如果请求中没有body,不能使用Multipart和FormEncoded
参数类型不能是基本类型和泛型
参数不能没有注解
URL不能为空
Form-encoded方法至少包含一个@Field注解
Multipart方法至少包含一个@Part注解
满足以上要求,才能正确构建ServiceMethod。
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;
  }

public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      responseConverter = createResponseConverter();
......

网络请求处理

方法解析完成之后,调用CallAdapter的adapt方法返回Call
内部构建如下
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);
      }
    };
  }
此时并没有发起请求,只是构建除了call,具体来说构建的是ExecutorCallbackCall,真正用来执行请求的是call内部的代理delegate,这个代理的真实身份是OkHttpCall,Retrofit2相当于将请求全权交给OKhttp处理,异步请求enqueue方法,同步请求execute方法都是执行的Okhttpclient对应的方法,
一下是okhttp请求的真实创建方法
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;
  }
异步执行完之后,利用callbackExecutor将回调抛回主线程。

Retrofit2比Retrofit简化了很多,但是功能却更加强大了。真的非常奇妙。作为网络请求框架,整个代码中居然没有用线程池,因为Okhttp本身对异步处理已经做的很好了。充分发挥了其它模块的功能,简化了自身逻辑。



欢迎扫描二维码,关注公众号





转载于:https://my.oschina.net/jiangch/blog/693679

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值