1.Retrofit
Retrofit是基于OkHttp封装的网络请求框架,网络请求的工作本质上是OkHttp完成的,而Retrofit仅负责网络请求接口的封装。
OkHttp的缺点:
①配置网络请求非常繁琐,尤其是配置复杂网络请求body、请求头、参数的时候。
②获取结果后需要用户手动解析ResponseBody,难以复用。
③无法自动进行线程切换。
④如果存在嵌套网络请求,就会陷入“回调地狱”。
Retrofit封装OkHttp主要优化了以下几点:
①build模式创建网络请求基本配置。
②用注解类组合HTTP网络请求。
③提供Gson解析返回的json数据。
④Executor完成线程切换。
Retrofit请求实现的核心在于注解、动态代理和反射。通过对接口层的封装,将请求参数、header、url这些网络请求信息封装,然后交给OkHttp完成实际的请求操作。在服务端返回数据后,OkHttp将原始的结果交给Retrofit,然后Retrofit根据不同的场景进行不同的数据解析方式。
使用Retrofit,开发者不用关注网络通信的细节,只需要在接口文件中声明一系列方法和返回值,然后通过注解的方式指定该方法对应哪个服务器接口,以及需要提供哪些参数。当在程序中调用该方法时,Retrofit会自动向对应的服务器接口发起请求,并将响应的数据解析成返回值声明的类型。这样可以用更加面向对象的思维来进行网络操作。
2.Retrofit用法
①添加依赖
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation("com.squareup.okhttp3:okhttp:4.9.1")
Retrofit是基于OkHttp二次封装的网络请求库, 所以要导入OkHttp的依赖。
gson是用来解析Json数据使用的。Retrofit也支持其他解析工具比如fastJson。
②build创建Retrofit实例
Retrofit mRetrofit = new Retrofit.Builder()
.baseUrl(Config.baseUrl)
.addConverterFactory( GsonConverterFactory.create())) //数据解析器
.callbackExecutor( Executors.newSingleThreadExecutor())//线程池
.build();
baseUrl:网络地址不能为空,且强制要求必须以斜杠/结尾。
callbackExecutor:使用单独的线程处理 (这很重要,一般网络请求如果不设置可能不会报错,但是如果是下载文件就会报错)。
③创建数据返回后的Bean类
public class MyLoginBean {
private int code;
private String msg;
public int getCode() { return code; }
public void setCode(int code) {
this.code = code;
}
public String getMsg() { return msg; }
public void setMsg(String msg) {
this.msg = msg;
}
}
④创建网络请求接口
public interface MyHttpList {
@FormUrlEncoded
@POST("test/login_test")
Call<MyLoginBean> login(@Field("username") String username, @Field("password") String password);
}
这是一个接口类,MyLoginBean是数据返回后的Bean类,Retrofit会自动使用导入的Gson解析。@Field("username") 为post值的key。每个参数只能有一个注解。
注意:@POST("test/login_test")路径最前面不能加斜杠/,否则它会自动裁剪路径,这样会导致路径错误。
Retrofit把网络请求的url分成两部分:第一部分是在创建Retrofit对象的时候,通过baseUrl设置的;另一部分就是在这个接口当中通过注解的形式传入的。注意,如果在接口中注解后面设置的url是一个完整的路径或完整的网址,那在创建Retrofit对象的时候就不需要设置baseUrl了。
⑥发起同步网络请求
MyHttpList httpList = mRetrofit.create( MyHttpList.class);
final Call<MyLoginBean> call = httpList.login("admin", "123456");
new Thread(new Runnable() {
@Override
public void run() {
Response<MyLoginBean> response = call.execute(); //同步请求网络
MyLoginBean bean = response.body();
Log.e(TAG, "onResponse: code=" + bean.getCode() + " message=" + bean.getMessage());
}
}).start();
首先用Retrofit.create()创建请求接口的代理,拿到call对象之后调用execute同步方法,返回一个response对象,然后通过response获取从后端请求返回的数据,因为gson解析器做好了解析,这里只需要response.body().getCode()、response.body().getMsg());就能获取服务端返回的json数据里面key对应value值。
create()方法内部使用到了观察者模式、代理模式以及外观模式。
注意:因为是同步方法,不能直接在ui线程里面操作,不然会造成线程阻塞直接报出anr,所以在使用call同步方法时,要开启一个子线程操作。
⑦发起异步网络请求
MyHttpList httpList = mRetrofit.create( MyHttpList.class);
Call<MyLoginBean> call = httpList.login( "admin", "123456");
call.enqueue(new Callback<MyLoginBean>() {
@Override
public void onResponse(Call<MyLoginBean> call, Response<MyLoginBean> response) {
MyLoginBean bean = response.body();
Log.e(TAG, "onResponse: code=" + bean.getCode() + " message=" + bean.getMessage());
}
@Override
public void onFailure(Call<MyLoginBean> call, Throwable t) {
Log.e(TAG, "onFailure: 网络请求失败="+t.getMessage());
}
});
调用call对象的enqueue异步方法,需要实现Callback接口里面的回调方法。因为是异步操作,所以不需要再开启线程来处理,框架已经处理好了。
⑧添加配置的OkHttpClient(主要使用请求超时/拦截器等功能)
如果Retrofit中需要设置请求超时时间、拦截器等时,可以使用OkHttp的方式来配置。
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.retryOnConnectionFailure(false)
.connectTimeout( 20,TimeUnit.SECONDS)
.readTimeout(20,TimeUnit.SECONDS)
.writeTimeout(20,TimeUnit.SECONDS)
.addInterceptor() //设置拦截器
.authenticator() //设置认证器
.proxy()//设置代理
.build();
mRetrofit = new Retrofit.Builder()
.client(okHttpClient)
……
.build();
3.源码分析
①构建Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL)
.callFactory(…)
.addConverterFactory(GsonConverterFacto ry.create())
.build();
Retrofit使用build()方法构建,通过设置各种工厂,为后面做好准备。
Retrofit.java:
public Retrofit build() {
//1.设置callFactory属性为.callFactory()传入的参数。如果没有指定callFactory则默认为OkHttpClient。如果需要对OkHttpClient进行详细的设置,就需要自己构建并传入OkHttpClient对象
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//2.设置callbackExecutor属性为.callbackExecutor()传入的参数。如果没有指定就获取platform(Android平台)的defaultCallbackExecutor,最终获取到的是一个MainThreadExecutor,它里面有一个主线程的Handler,调用MainThreadExecutor的execute就可以使用Handler向主线程post一个任务
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
//3.设置callAdapterFactories属性,其中CallAdapter.Factory主要用于对retrofit.Call对象进行转化,其中的优先级顺序为:①外界传入的callAdapterFactories②platform.defaultCallAdapterFactories()
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll( platform.defaultCallAdapterFactories(callbackExecutor));
//4.设置converterFactories属性,其中Converter.Factory主要用于转化数据,例如将返回的ResponseBody转化为对象等。其中的优先级顺序:①BuiltInConverters②外界传入的converterFactories③platform.defaultConverterFactories()
List<Converter.Factory> converterFactories = new ArrayList<>(1+this.converterFactories.size() + platform.defaultConverterFactoriesSize());
converterFactories.add(new BuiltInConverters());
converterFactories.addAll( this.converterFactories);
converterFactories.addAll( platform.defaultConverterFactories());
//5.通过设置的这些参数构建Retrofit对象并返回
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
build方法主要给几个重要参数赋值,分别是:callFactory、callbackExecutor、callAdapterFactories、converterFactories。
callFactory是网络请求器的工厂,用于生产Call请求对象,不管是同步请求还是异步请求都是通过这个Call来调用的。从这里也可以看出,在Retrofit内部默认使用的是okhttp的请求方式。
adapterFactories是网络请求适配器工厂的集合,用于放置网络请求适配器工厂。网络请求适配器工厂是用于生产CallAdapter网络请求适配器的。
converFactories是数据转换器工厂的集合,用于放置数据转换器工厂。数据转换器工厂是用于生产数据转换器的,比如可以放入Gson作为转换器,服务器返回数据为Json类型,然后通过Gson解析器进行解析,并在主线程中去显示。
callbackExecutor是回调方法的执行器,主线程切换到子线程,子线程切换到主线程,都是通过这个Executor来执行的。
②retrofit.create()生成代理对象
使用Retrofit进行网络请求的时候,需要先定义一个接口类,然后调用retrofit.create(serviceClass)生成接口的实例。retrofit.create()把网络请求配置的接口转换成一个实现了该接口的对象,使用的核心技术就是JDK动态代理。
Retrofit.java:
public <T> T create(final Class<T> service) {
// 动态代理,返回代理对象
return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() {
private final Object[] emptyArgs = new Object[0]; // 定义空的参数数组
//每次调用接口中的方法都会走该方法,相当于就是接口方法的实现。参数proxy:代理对象,method:当前调用的接口方法,args:方法的参数
@Override public Object invoke(Object proxy, Method method, Object[] args) {
if (method.getDeclaringClass() == Object.class) { // 如果是Object类中的方法则不做额外处理
return method.invoke(this, args);
}
// 重点,获取一个ServiceMethod对象,然后调用它的invoke方法,网络请求的具体实现、处理就在这里
return loadServiceMethod(method) .invoke(args != null ? args : emptyArgs);
}
});
}
retrofit.create()方法创建并返回了一个代理对象。在InvocationHandler
的invoke
方法里调用了loadServiceMethod
方法获取一个ServiceMethod
对象,一个ServiceMethod
对象就代表网络请求接口里的一个方法,获取到ServiceMethod
后,调用它的invoke
方法进行网络请求。