前言
Volley
现在虽然比较少用了,但有些老项目还是有在使用,新项目一般都是使用OkHttp,Volley在Android 2.3以上使用的是HttpURLConnection
,而2.3以下则是使用HttpClient
实现,而HttpClient
在Andoid6.0时已经被废弃了,但Volley内部是统一返回HttpClient的响应类的,所以还是需要加上HttpClient的依赖。
OkHttp
拥有比HttpURLConnection
和HttpClient
更好的性能,而Volley
可以兼容HttpURLConnection
和HttpClient
,那是否可以兼容OkHttp
呢?如果可以兼容,那以前使用Volley
的请求,将拥有更好的性能
答案是可以的!经过上一篇Volley 源码分析,我们知道Volley的兼容不同的网络请求框架是通过一个HttpStack
接口来实现的,不同的网络请求框架只要创建一个实现类,在创建请求队列Volley.newRequestQueue(context, stack)
的时候传入即可,这种模式叫策略模式,通过一个接口来解耦具体的网络请求框架,不同框架的具体实现逻辑都内聚在具体实现类中。
问题
Volley只是一个网络请求工具框架,并不是底层的网络请求实现,所以Volley对标的是例如Retrofit、OkGo、RxHttp等,他们是封装了底层的网络请求实现的上层框架
底层网络请求实现,一般有HttpURLConnection、HttpClient还有OkHttp,它们一般是使用Socket来对Http协议进行实现,所以更加底层
回顾
简单来回顾一下HttpStack
接口,HttpStack接口内只有一个performRequest()
方法,该方法在子线程中回调,所以需要进行同步网络请求,返回统一的HttpResponse响应实例即可
/**
* Http协议栈接口
*/
public interface HttpStack {
/**
* 使用指定参数,执行Http请求
*
* request.getPostBody() == null,则发送GET请求,不为null则发送POST请求
* 并且,Content-Type请求头的取值从 request.getPostBodyContentType() 中获取
*
* @param request 请求对象
* @param additionalHeaders 附带的请求头
* @return HTTP的响应
*/
HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
实现
创建一个类OkHttpStack
,使用OkHttp发起请求的具体实现都在该类中,具体要复写performRequest()
方法,具体做了以下几件事:
- 把请求对象和请求头的信息转接到OkHttp的Request请求对象中,设置请求方法、请求参数等,再发起请求,获取OkHttp响应Response对象
- 把OkHttp响应Response对象的,状态行、响应体、响应码等信息,转换为HttpClient的HttpResponse响应对象
/**
* OkHttp实现的Volley网络层
*/
public class OkHttpStack implements HttpStack {
private OkHttpClient mOkHttpClient;
/**
* 无参构造,没有传入OkHttpClient,直接创建默认的OkHttpClient实例
*/
public OkHttpStack() {
this(new OkHttpClient.Builder().build());
}
/**
* 外部传入OkHttpClient,则使用它来构建请求
*/
public OkHttpStack(OkHttpClient okHttpClient) {
if (okHttpClient == null) {
throw new IllegalArgumentException("OkHttpClient can't be null");
}
mOkHttpClient = okHttpClient;
}
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
int timeoutMs = request.getTimeoutMs();
OkHttpClient client;
//3个超时时间都不一样时,重新构建一个OkHttpClient,才可以设置
if (timeoutMs != mOkHttpClient.connectTimeoutMillis() &&
timeoutMs != mOkHttpClient.readTimeoutMillis() &&
timeoutMs != mOkHttpClient.writeTimeoutMillis()) {
client = mOkHttpClient.newBuilder()
.connectTimeout(timeoutMs, TimeUnit.MILLISECONDS)
.readTimeout(timeoutMs, TimeUnit.MILLISECONDS)
.writeTimeout(timeoutMs, TimeUnit.MILLISECONDS)
.build();
} else {
client = mOkHttpClient;
}
//下次以最新请求的配置为准
mOkHttpClient = client;
//创建OkHttp的请求
okhttp3.Request.Builder builder = new okhttp3.Request.Builder();
//设置请求Url
builder.url(request.getUrl());
//添加请求Header
Map<String, String> headers = request.getHeaders();
for (String name : headers.keySet()) {
builder.addHeader(name, headers.get(name));
}
for (String name : additionalHeaders.keySet()) {
builder.addHeader(name, additionalHeaders.get(name));
}
//设置请求的参数
setConnectionParametersForRequest(builder, request);
//构建请求
okhttp3.Request okRequest = builder.build();
Call call = client.newCall(okRequest);
//发起同步请求,获取相应
Response okResponse = call.execute();
//转换相应状态行
BasicStatusLine responseStatus = new BasicStatusLine(
//把OkHttp的网络协议,转为HttpClient的网络协议类
parseProtocol(okResponse.protocol()),
//响应码
okResponse.code(),
//消息
okResponse.message()
);
//把OkHttp的请求结果转换成HttpClient的请求结果
BasicHttpResponse httpClientResponse = new BasicHttpResponse(responseStatus);
//OkHttp响应转换为HttpClient的HttpEntity对象
httpClientResponse.setEntity(entityFromOkHttpResponse(okResponse));
//响应头转换
Headers responseHeaders = okResponse.headers();
int size = responseHeaders.size();
for (int i = 0; i < size; i++) {
String name = responseHeaders.name(i);
String value = responseHeaders.value(i);
httpClientResponse.addHeader(new BasicHeader(name, value));
}
return httpClientResponse;
}
/**
* OkHttp响应转换为HttpClient的HttpEntity对象
*/
private static HttpEntity entityFromOkHttpResponse(Response response) throws IOException {
BasicHttpEntity entity = new BasicHttpEntity();
ResponseBody body = response.body();
//响应体信息
entity.setContent(body.byteStream());
entity.setContentLength(body.contentLength());
entity.setContentEncoding(response.header("Content-Encoding"));
//Content-Type
if (body.contentType() != null) {
entity.setContentType(body.contentType().type());
}
return entity;
}
/**
* 设置请求的参数
*/
static void setConnectionParametersForRequest(okhttp3.Request.Builder builder, Request<?> request) throws IOException, AuthFailureError {
//根据不同的请求方法,设置请求类型和请求体(POST、PUT、PATCH请求的参数放在请求体,而GET请求等是放在URL里面的,所以这里只设置有请求体的请求方法)
switch (request.getMethod()) {
case Request.Method.DEPRECATED_GET_OR_POST:
byte[] postBody = request.getPostBody();
if (postBody != null) {
builder.post(RequestBody.create(MediaType.parse(request.getPostBodyContentType()), postBody));
}
break;
case Request.Method.GET:
builder.get();
break;
case Request.Method.DELETE:
builder.delete();
break;
case Request.Method.POST:
builder.post(createRequestBody(request));
break;
case Request.Method.PUT:
builder.put(createRequestBody(request));
break;
case Request.Method.HEAD:
builder.head();
break;
case Request.Method.OPTIONS:
builder.method("OPTIONS", null);
break;
case Request.Method.TRACE:
builder.method("TRACE", null);
break;
case Request.Method.PATCH:
builder.patch(createRequestBody(request));
break;
default:
throw new IllegalStateException("Unknown method type.");
}
}
/**
* 把OkHttp的网络协议,转为HttpClient的网络协议类
*/
private static ProtocolVersion parseProtocol(final Protocol protocol) {
switch (protocol) {
case HTTP_1_0:
return new ProtocolVersion("HTTP", 1, 0);
case HTTP_1_1:
return new ProtocolVersion("HTTP", 1, 1);
case SPDY_3:
return new ProtocolVersion("SPDY", 3, 1);
case HTTP_2:
return new ProtocolVersion("HTTP", 2, 0);
}
throw new IllegalAccessError("Unkwown protocol");
}
/**
* 通过OkHttp的请求,创建RequestBody
*/
private static RequestBody createRequestBody(Request<?> request) throws AuthFailureError {
final byte[] body = request.getBody();
if (body == null) {
throw new NullPointerException(request.getMethod() + "请求的请求体不能为空");
}
return RequestBody.create(MediaType.parse(request.getBodyContentType()), body);
}
}
使用
- 创建请求队列
- 创建请求,设置回调
- 把请求加入到请求队列中,发起请求
//创建请求队列
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext(), new OkHttpStack());
String url = "https://www.wanandroid.com/article/list/" + page + "/json";
//创建请求,设置回调
StringRequest request = new StringRequest(url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
//请求成功
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
//请求失败
}
});
//加入队列,发起请求
requestQueue.add(request);
网友评论