Volley支持HTTP/2

volley暂时是不支持HTTP/2的,这是官方的解释Does volley Support HTTP/2
不过volley设计的扩展性特别好,volley提供的接口HttpStack,可以自己去实现来支持HTTP/2。在Android上,从4.4以后系统就已经支持HTTP/2,但是不稳定,有bug,直到Android5.0才修复bug。第三方库中,有OkHttp 是已经支持了HTTP/2,所以我们可以使用OkHttp 来实现。
具体实现,可以参考OkHttp3Stack
自己的实现如下:

@SuppressWarnings("deprecation")
public class OkHttpStack implements HttpStack {
    private final OkHttpClient mOkHttpClint;

    public OkHttpStack(OkHttpClient okHttpClient) {
        this.mOkHttpClint = okHttpClient;
    }

    public OkHttpStack() {
        mOkHttpClint = new OkHttpClient();
    }

    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
        OkHttpClient.Builder builder = mOkHttpClint.newBuilder();
        int timeoutMs = request.getTimeoutMs();
        builder.connectTimeout(timeoutMs, TimeUnit.MILLISECONDS);
        builder.readTimeout(timeoutMs, TimeUnit.MILLISECONDS);
        builder.writeTimeout(timeoutMs, TimeUnit.MILLISECONDS);
        okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder();
        requestBuilder.url(request.getUrl());
        Map<String, String> headers = request.getHeaders();
        if (headers != null) {
            for (String key : headers.keySet()) {
                String value = headers.get(key);
                if (TextUtils.isEmpty(value)) {
                    continue;
                }
                requestBuilder.addHeader(key, value);
            }
        }
        if (additionalHeaders != null) {
            for (String key : additionalHeaders.keySet()) {
                String value = additionalHeaders.get(key);
                if (TextUtils.isEmpty(value)) {
                    continue;
                }
                requestBuilder.addHeader(key, value);
            }
        }
        setConnectionParametersForRequest(requestBuilder, request);
        okhttp3.Request okRequest = requestBuilder.build();
        OkHttpClient okClient = builder.build();
        Call call = okClient.newCall(okRequest);
        Response okResponse = call.execute();
        BasicStatusLine statusLine = new BasicStatusLine(parseProtocol(okResponse.protocol()), okResponse.code(), okResponse.message());
        BasicHttpResponse httpResponse = new BasicHttpResponse(statusLine);
        httpResponse.setEntity(entityFromOkHttpResponse(okResponse));
        Headers okHeaders = okResponse.headers();
        for (int i = 0; i < okHeaders.size(); i++) {
            String name = okHeaders.name(i);
            String value = okHeaders.value(i);
            if (name != null) {
                httpResponse.addHeader(new BasicHeader(name, value));
            }
        }
        return httpResponse;
    }

    @SuppressWarnings("deprecation")
    /* package */ private static void setConnectionParametersForRequest( /*HttpURLConnection connection*/
            okhttp3.Request.Builder builder, Request<?> request) throws IOException, AuthFailureError {
        CUtils.logD("OK","---request url : " + request.getUrl());
        switch (request.getMethod()) {
            case Request.Method.DEPRECATED_GET_OR_POST:
                byte[] postBody = request.getPostBody();
                if (postBody != null) {
                    // connection.setRequestMethod("POST");
                    // addBody(connection, request, postBody);
                    builder.post(RequestBody.create(MediaType.parse(request.getPostBodyContentType()), postBody));
                }
                break;
            case Request.Method.GET:
                // Not necessary to set the request method because connection defaults to GET but
                // being explicit here.
//                connection.setRequestMethod("GET");
                builder.get();
                break;
            case Request.Method.DELETE:
//                connection.setRequestMethod("DELETE");
                builder.delete();
                break;
            case Request.Method.POST:
//                connection.setRequestMethod("POST");
//                addBodyIfExists(connection, request);
                builder.post(addBodyIfExists(request));
                break;
            case Request.Method.PUT:
//                connection.setRequestMethod("PUT");
//                addBodyIfExists(connection, request);
                builder.put(addBodyIfExists(request));
                break;
            case Request.Method.HEAD:
//                connection.setRequestMethod("HEAD");
                builder.head();
                break;
            case Request.Method.OPTIONS:
//                connection.setRequestMethod("OPTIONS");
                builder.method("OPTIONS", null);
                break;
            case Request.Method.TRACE:
//                connection.setRequestMethod("TRACE");
                builder.method("TRACE", null);
                break;
            case Request.Method.PATCH:
//                connection.setRequestMethod("PATCH");
//                addBodyIfExists(connection, request);
                builder.patch(addBodyIfExists(request));
                break;
            default:
                throw new IllegalStateException("Unknown method type.");
        }
    }


    private static @Nullable
    RequestBody addBodyIfExists(Request<?> request) throws AuthFailureError {
        byte[] body = request.getBody();
        if (body == null) {
            return null;
        }
        return RequestBody.create(MediaType.parse(request.getBodyContentType()), body);
    }

    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");
    }

    private static HttpEntity entityFromOkHttpResponse(Response r) throws IOException {
        BasicHttpEntity entity = new BasicHttpEntity();
        ResponseBody body = r.body();

        entity.setContent(body.byteStream());
        entity.setContentLength(body.contentLength());
        entity.setContentEncoding(r.header("Content-Encoding"));

        if (body.contentType() != null) {
            entity.setContentType(body.contentType().type());
        }
        return entity;
    }


}

注意
如果项目使用了Tinker,请注意下面所写

	在Android 9.0上面Apache HTTP 客户端弃用,bootclasspath 中已移除(org.apache.http.legacy.boot.jar)
	请使用gradle配置,使用`  useLibrary 'org.apache.http.legacy'`,而不要再libs中放置`org.apache.http.legacy.jar ` jar包
	

 // Apache HTTP 客户端弃用,bootclasspath 中已移除
    //AndroidP开始org.apache.http.legacy.jar这个包不在打包进bootclasspath,对于targetsdkversion>=28的,继续引用此包的应用将直接出现NoClassDefFoundError,
    // 对于targetsdkversion低于28的,系统会将此包加载到应用的classloader中,如果应用采用系统的classloader加载应用,将同样会出现NoClassDefFoundError,建议模块使用 HttpURLConnection 类,更加高效。
    //移除libs中的org.apache.http.legacy.jar,保证自己apk中没有这个jar,否则在android9.0上热更新之后会导致此jar中的org.apache.http.ProtocolVersion会参与运行,就会抛异常,参考源码
    /**
     * public ProtocolVersion(String protocol, int major, int minor) {*         throw new RuntimeException("Stub!");
     *}* */
    //这个jar其实只是用于编译,不能参与运行
    //引用github的回答
    //依赖的三方库中有HttpClient ,引入的时候有人在项目中加入了org.apache.http.legacy.jar 。
    //
    //解决方案:删除 org.apache.http.legacy.jar 。 用 useLibrary 'org.apache.http.legacy' 或者 想办法compileOnly org.apache.http.legacy.jar ,或者找一份 httpclient源码导入项目中、会增加包体积,并不会所谓『冲突』。
    //保证apk内没有legacy.jar class文件
    //
    //问题原因 9.0 之前 /system/framework/org.apache.http.legacy.boot.jar 一直在 bootclasspath 中,在9.0 移除了。这个jar包出现在 app PathClassloader DexListPath 中。 tinker 在类 AndroidNClassLoader 方法 recreateDexPathList 在重建 DexPathList的时候有一个判断
    //if (!dexFile.getName().equals(baseApkFullPath)) {
    //continue;
    //}
    //这个判断会把原先 PathClassLoader 中的 /system/framework/org.apache.http.legacy.boot.jar 过滤掉。从而导致 在 findClass的时候 加载到打到 apk 内的 org.apache.http.legacy.jar (注意这个只是个头文件) 这个jar本来是只是参与编译的,现在他参与运行的 所以抛出了上述异常。假设没有将 org.apache.http.legacy.jar这个包打到apk内,在 super.findClass的时候会抛异常,tinker 让originClassLoader.loadClass(name) 并且originClassLoader 这个原先的 PathClassLoader 中是有/system/framework/org.apache.http.legacy.boot.jar 。分析到这我也说不清楚算不算tinker的bug了,如果正确使用httpclient的话,是不会有此问题的。以上方案针对targetSdk < 28 。
    //最后,请尽快迁移 httpclient。
    //https://github.com/Tencent/tinker/issues/957
    useLibrary 'org.apache.http.legacy'

在AndroidManifest文件中添加

    <uses-library
        android:name="org.apache.http.legacy"
        android:required="false" />

致敬大佬,努力前行!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值