[置顶] Retrofit2使用方式和源码解析
16lz
2021-01-25
简单介绍
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 默认使用okhttpRetrofit2 默认使用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-gsonJackson: com.squareup.retrofit:converter-jacksonMoshi: com.squareup.retrofit:converter-moshiProtobuf: com.squareup.retrofit:converter-protobufWire: com.squareup.retrofit:converter-wireSimple 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本身对异步处理已经做的很好了。充分发挥了其它模块的功能,简化了自身逻辑。
欢迎扫描二维码,关注公众号
更多相关文章
- 跨平台移动开发_Android(安卓)平台使用 PhoneGap 方法
- Android开发笔记: Project "XXX" is missing required source fo
- android 解决依赖冲突
- 我的android——OpenGL(2)——gl10方法解析
- android EditText里面嵌入两个按钮,通过按钮可以加减EditText里的
- android GBK转UTF-8出现乱码问题解决方法
- 解决升级 Android(安卓)Studio 3.6.1 后无法运行 Java 代码的问
- Android(安卓)project 的常用编译方法
- android修改虚拟内存(方法)