OkHttp
简介
OkHttp是时下最火的Http请求框架,其官网及源码地址如下:
相比于其他网络请求框架,OkHttp有什么优点呢?官方介绍如下:
- HTTP/2 support allows all requests to the same host to share a socket.
- Connection pooling reduces request latency (if HTTP/2 isn’t available).
- Transparent GZIP shrinks download sizes.
- Response caching avoids the network completely for repeat requests.
从上面的介绍我们可以得出有下面这些优点:
- 在Http/2的支持下,如果网络请求的host是同一个时,允许这些请求共用一个socket。
- 使用连接池减少请求延迟。
- 透明的GZIP压缩,减少数据流量。
- 支持响应缓存,避免重复的网络请求。
使用
添加依赖
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
GET请求
//创建OkHttpClient对象
OkHttpClient httpClient = new OkHttpClient();
//创建Request对象
Request request = new Request.Builder()
.url("http://www.baidu.com")
.addHeader("key", "value")
.get()
.build();
//get方法同步请求
try {
Response response = httpClient.newCall(request).execute();
if (response.isSuccessful()){
String result = response.body().string();
}
} catch (IOException e) {
e.printStackTrace();
}
//get方法异步请求,异步请求的回调方法均在子线程中执行
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//请求失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//请求成功
String result = response.body().string();
}
});
POST请求
//post请求
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
String data = "{key:value}";
RequestBody requestBody = RequestBody.create(mediaType, data);
Request request1 = new Request.Builder()
.url("http://www.baidu.com")
.post(requestBody)
.build();
//post同步请求
try {
Response response = httpClient.newCall(request1).execute();
if (response.isSuccessful()) {
String result = response.body().string();
}
} catch (IOException e) {
e.printStackTrace();
}
//post异步请求
httpClient.newCall(request1).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//请求失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//请求成功
String result = response.body().string();
}
});
拦截器、超时、缓存机制
OKHttp的拦截器分为应用拦截器(Application interceptors)和网络拦截器(Network Interceptors),下图是官方对拦截器的介绍。
两种拦截器的相对优势官方说明如下:
Each interceptor chain has relative merits.
Application interceptors
- Don’t need to worry about intermediate responses like redirects and retries.
- Are always invoked once, even if the HTTP response is served from the cache.
- Observe the application’s original intent. Unconcerned with OkHttp-injected headers like If-None-Match.
- Permitted to short-circuit and not call Chain.proceed().
- Permitted to retry and make multiple calls to Chain.proceed().
Network Interceptors
- Able to operate on intermediate responses like redirects and retries.
- Not invoked for cached responses that short-circuit the network.
- Observe the data just as it will be transmitted over the network.
- Access to the Connection that carries the request.
翻译过来为:
应用拦截器
- 不用关系redirects和retries的中间过程
- 永远只会执行一次,不管response是否从缓存中获取
- 可以监控原始的请求,不关心其它诸如If-None-Match的Header
- 允许不调用Chain.proceed()
- 允许重试多次调用Chain.proceed()
网络拦截器
- 可以操作redirects和retries的过程
- 不会调用缓存的response
- 可以监控网络传输交互的数据
- 可以获取Connection携带的请求信息
它们的区别在于:Application Interceptor拦截应用层与OkHttp之间的网络响应;而Network Interceptor拦截OkHttp与网络层之间的网络请求响应。应用拦截器通过addInterceptor()的方法添加,而网络拦截器通过addNetworkInterceptor()方法添加。
//创建拦截器
Interceptor logInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request2 = chain.request();
Logger logger = Logger.getGlobal();
long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s"
, request2.url(), chain.connection(), request2.headers()));
Response response = chain.proceed(request2);
long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
};
//为OkHttpClient添加拦截器、超时、缓存控制
Cache cache = new Cache(new File(Environment.getDataDirectory(), "cache"), 10 * 10 * 1024);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)//连接超时时间
.writeTimeout(10, TimeUnit.SECONDS)//写操作超时时间
.readTimeout(10, TimeUnit.SECONDS)//读操作超时时间
.cache(cache)//添加缓存
// .addInterceptor(logInterceptor)//添加应用拦截器
.addNetworkInterceptor(logInterceptor)//网络拦截器
.build();
以下是将logInterceptor分别添加成应用拦截器和网络拦截器所得到的打印信息:
//应用拦截器
03-07 08:00:37.760 2550-2606/com.example.study I/global: Sending request http://www.baidu.com/ on null
03-07 08:00:37.850 2550-2606/com.example.study I/global: Received response for http://www.baidu.com/ in 85.0ms
Server: bfe/1.0.8.18
Date: Thu, 07 Mar 2019 13:00:36 GMT
Content-Type: text/html
Last-Modified: Mon, 23 Jan 2017 13:28:12 GMT
Transfer-Encoding: chunked
Connection: Keep-Alive
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Pragma: no-cache
Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
//网络拦截器
03-07 08:04:22.471 2812-2849/com.example.study I/global: Sending request http://www.baidu.com/ on Connection{www.baidu.com:80, proxy=DIRECT@ hostAddress=www.baidu.com/183.232.231.172:80 cipherSuite=none protocol=http/1.1}
Host: www.baidu.com
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/3.11.0
03-07 08:04:22.511 2812-2849/com.example.study I/global: Received response for http://www.baidu.com/ in 41.8ms
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html
Date: Thu, 07 Mar 2019 13:04:22 GMT
Last-Modified: Mon, 23 Jan 2017 13:28:12 GMT
Pragma: no-cache
Server: bfe/1.0.8.18
Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
Transfer-Encoding: chunked
对比两个打印信息发现Sending Request最后的on后面跟的connection应用拦截器为null,而网络拦截器不为null,这说明应用拦截器是先拦截后发送请求的,而网络拦截器则是先发送请求后拦截信息的。
需要注意的是,OkHttp请求返回的response.body().string()方法只能调用一次,若调用两次将会抛出异常。除了使用response.body().string()获取String数据外,还可以通过response.body().bytes()和response.body().byteStream()分别获取字节数组和InputStream输入流。关于OkHttp的更多用法可以到官方的wiki中进一步的了解。
Retrofit
Retrofit是一个基于OkHttp的RESTFUL网络加载框架,它的网络请求工作是通过OkHttp完成的,而Retrofit仅负责网络请求接口的封装。
使用
添加依赖
implementation 'com.squareup.retrofit2:retrofit:2.1.0'//retrofit
implementation 'com.google.code.gson:gson:2.6.2'//Gson 库
//下面两个是RxJava 和RxAndroid
implementation 'io.reactivex:rxjava:1.1.0'
implementation 'io.reactivex:rxandroid:1.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'//转换器,请求结果转换成Model
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'//配合Rxjava 使用
创建请求接口
public interface ApiService {
//使用GET方法请求数据,配合@Query和@QueryMap使用
@GET("top250")
Call<DataBean> getTop250(@Query("start") int start, @Query("count") int count);
//使用POST方法发送form-encoded请求数据,@FormUrlEncoded可以配合@Filed和@FieldMap使用
@POST("top250")
@FormUrlEncoded
Call<DataBean> getTop250ByPost(@Field("start") int start, @Field("count") int count);
//使用POST方法发送自定义数据类型请求数据
@POST("top250")
Call<DataBean> getTop250ByPostBody(@Body DataBean data);
}
使用Retrofit请求数据
String baseUrl = "https://api.douban.com/v2/movie/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService apiService = retrofit.create(ApiService.class);
Call<DataBean> top250 = apiService.getTop250(1, 10);
//同步请求
try {
Response<DataBean> execute = top250.execute();
if (execute.isSuccessful()) {
DataBean result = execute.body();
}
} catch (IOException e) {
e.printStackTrace();
}
//异步请求
top250.enqueue(new Callback<DataBean>() {
@Override
public void onResponse(Call<DataBean> call, Response<DataBean> response) {
//方法在主线程中回调
DataBean result = response.body();
}
@Override
public void onFailure(Call<DataBean> call, Throwable t) {
//方法在主线程中回调
}
});
Retrofit的请求方法与请求参数均在请求接口中声明好了,在使用过程中,只需要通过Retrofit的实例获取到请求接口类的代理对象,即可通过代理对象进行网络请求,请求也分为同步和异步两种方式。另外,Retrofit底层默认使用OkHttp进行网络请求,我们可以配置自己的OkHttp各项参数,通过clinet()方法将Retrofit的OkHttpClient设置为我们自己的OkHttpClient。
参考资料: