美文网首页安卓开发博客
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