美文网首页安卓开发博客
Volley使用OkHttp进行网络请求

Volley使用OkHttp进行网络请求

作者: h2coder | 来源:发表于2021-09-05 17:54 被阅读0次

前言

Volley现在虽然比较少用了,但有些老项目还是有在使用,新项目一般都是使用OkHttp,Volley在Android 2.3以上使用的是HttpURLConnection,而2.3以下则是使用HttpClient实现,而HttpClient在Andoid6.0时已经被废弃了,但Volley内部是统一返回HttpClient的响应类的,所以还是需要加上HttpClient的依赖。

OkHttp拥有比HttpURLConnectionHttpClient更好的性能,而Volley可以兼容HttpURLConnectionHttpClient,那是否可以兼容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);

总结

相关文章

网友评论

    本文标题:Volley使用OkHttp进行网络请求

    本文链接:https://www.haomeiwen.com/subject/dyojwltx.html