再见,volley。你好square全家桶。

来源:互联网 发布:手机淘宝开店怎么开店 编辑:程序博客网 时间:2024/05/17 21:58

好久没有写博客了,因为前段时间在找工作,最近刚刚算是稍微稳定下来,在工作中我最近在学习百度地图,ArcGIS,OpenCV这些东西的用法,对以前自学的主流Android开发的知识用到的甚少,不过我自己也没有懈怠,在业余时间还是在学习主流的应用开发技术和编写代码。

本篇文章估计是我最后一次写有关Volley的,为什么这么说。主要是因为这个库,随着时间的推移,已经越来越显现出老态。大家可以发现,Volley和Gson这两个Google自家出品的库已经更新的越来越慢,甚至是好久都没有什么重大改动。最明显的一点,也是我今天重点要说的就是对OkHttp的支持问题。

Google官方早就不建议使用HttpClient来完成网络请求,更是在Android 6.0版本中把它彻底从SDK中删除,而另一种方式HttpURLConnection虽然在高版本中没有明显bug,但是它以使用复杂著称,特别的难用。这时候square出品的okhttp则是力压两种原生的Http底层封装API,被众多开发者广泛的接受,不仅如此,据说Android4.4的源码中已经把默认的HttpURLConnection实现换成OkHttp实现了,可见Google官方已经承认了OkHttp的强大的存在。

从上面可以看出来,在2016年,特别是现在,都2016年年底,马上2017了,如果你的应用还没有用上OkHttp,那就真的有点落伍了。同样,如果一个网络封装库,它不支持OkHttp,那这个库就真的该让我们考虑考虑了。

什么?你说Volley支持OkHttp?那你一定是从知乎或者微信公众号过来的。今天我就来具体说明Volley在支持OkHttp的支持性上有多差。

首先,在OkHttp 2.x时代,Volley的确是全面支持OkHttp,你编写一个OkHttpStack类,用它继承Volley的HurlStack类,然后短短几十行代码就可以写出一个OkHttpStack类,如下所示:

package com.icon.app.util;import com.android.volley.toolbox.HurlStack;import com.squareup.okhttp.OkHttpClient;import com.squareup.okhttp.OkUrlFactory;import java.io.IOException;import java.net.HttpURLConnection;import java.net.URL;/** * An {@link com.android.volley.toolbox.HttpStack HttpStack} implementation * which uses OkHttp as its transport. */public class OkHttpStack extends HurlStack {    private final OkUrlFactory okUrlFactory;    public OkHttpStack() {        this(new OkUrlFactory(new OkHttpClient()));    }    public OkHttpStack(OkUrlFactory okUrlFactory) {        if (okUrlFactory == null) {            throw new NullPointerException("Client must not be null.");        }        this.okUrlFactory = okUrlFactory;    }    @Override    protected HttpURLConnection createConnection(URL url) throws IOException {        return okUrlFactory.open(url);    }}

基本思想就是利用了OkUrlFactory这个类,用它调用open方法并传入url参数可以得到一个HttpURLConnection的对象。

Ok,如果你只想使用OkHttp 2的话,这就可以了,后面也不用继续看了。

但是我想你一定知道OkHttp的最新版本是3.5.0。既然有了最新版,我们就应该使用它,特别是大版本号的更新往往代表着重大升级。但是问题来了,Volley对于okhttp3的支持变得非常牵强!

首先,第一种写法,也就是按照上面的写法,OkUrlFactory被注解成了过时的类,虽然很多情况下过时的API也是可以使用的,但是这个类强行使用就会报错,导致程序崩溃。于是,你说,我们用okhttp-connection 2.x的OkUrlFactory,但OkHttpClient用OkHttp3.x的不就行了吗,但是并不行,因为2和3的包名不一样,2只能和2的配套使用,3只能和3的配套使用。既然不行,那就只能换写法。

于是,我在网上找到了第二种写法:

package com.example.allen.volleymanager.volley;import com.android.volley.AuthFailureError;import com.android.volley.Request;import com.android.volley.toolbox.HttpStack;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.ProtocolVersion;import org.apache.http.StatusLine;import org.apache.http.entity.BasicHttpEntity;import org.apache.http.message.BasicHeader;import org.apache.http.message.BasicHttpResponse;import org.apache.http.message.BasicStatusLine;import java.io.IOException;import java.util.Map;import java.util.concurrent.TimeUnit;import okhttp3.Headers;import okhttp3.MediaType;import okhttp3.OkHttpClient;import okhttp3.Protocol;import okhttp3.RequestBody;import okhttp3.Response;import okhttp3.ResponseBody;/** * Created by Allen Lin on 2016/02/17. */public class OkHttp3Stack implements HttpStack {    private final OkHttpClient mClient;    public OkHttp3Stack(OkHttpClient client) {        this.mClient = client;    }    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"));        if (body.contentType() != null) {            entity.setContentType(body.contentType().type());        }        return entity;    }    @SuppressWarnings("deprecation")    private static void setConnectionParametersForRequest            (okhttp3.Request.Builder builder, Request<?> request)            throws IOException, AuthFailureError {        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.");        }    }    private static RequestBody createRequestBody(Request request) throws AuthFailureError {        final 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");    }    @Override    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)            throws IOException, AuthFailureError {        int timeoutMs = request.getTimeoutMs();        OkHttpClient client = mClient.newBuilder()                .readTimeout(timeoutMs, TimeUnit.MILLISECONDS)                .connectTimeout(timeoutMs, TimeUnit.MILLISECONDS)                .writeTimeout(timeoutMs, TimeUnit.MILLISECONDS)                .build();        okhttp3.Request.Builder okHttpRequestBuilder = new okhttp3.Request.Builder();        Map<String, String> headers = request.getHeaders();        for (final String name : headers.keySet()) {            okHttpRequestBuilder.addHeader(name, headers.get(name));        }        for (final String name : additionalHeaders.keySet()) {            okHttpRequestBuilder.addHeader(name, additionalHeaders.get(name));        }        setConnectionParametersForRequest(okHttpRequestBuilder, request);        okhttp3.Request okhttp3Request = okHttpRequestBuilder.url(request.getUrl()).build();        Response okHttpResponse = client.newCall(okhttp3Request).execute();        StatusLine responseStatus = new BasicStatusLine                (                        parseProtocol(okHttpResponse.protocol()),                        okHttpResponse.code(),                        okHttpResponse.message()                );        BasicHttpResponse response = new BasicHttpResponse(responseStatus);        response.setEntity(entityFromOkHttpResponse(okHttpResponse));        Headers responseHeaders = okHttpResponse.headers();        for (int i = 0, len = responseHeaders.size(); i < len; i++) {            final String name = responseHeaders.name(i), value = responseHeaders.value(i);            if (name != null) {                response.addHeader(new BasicHeader(name, value));            }        }        return response;    }}

这个写法直接实现HttpStack接口,看起来屌屌的,但是你应该发现了,它使用了大量apache的API,这些API在在Android 6.0版本中都被删除了,根本用不了,于是我想了个办法,把这个类不放在自己的包下,而是放在Volley的包下,于是,编译期确实没有了问题,跑起来以后发送普通的网络请求也是没有问题的,但是,一旦使用volley加载图片就会报NoSuchMehodError这个迷之错误,我花了好长时间也发现不了到底是哪里报出的这个错误,于是又只能放弃这个写法。

再接再厉,第三种写法。这第三种写法,更牛逼,volley不是不支持okhttp吗,我把volley全改了,什么,Volley类,BaseNetwork类,OkHttpStack类等等全是自己写的,他这个严格意义上来说已经不属于对volley的扩展了,而是把它的骨架都换了,但是如果能成功运行倒是可以,可惜的是,他还是使用了apache的API,导致不能用,如果还像上面一样,把它放在volley的包下,我觉得很不爽,因为代码太多,这么多代码放过去,volley都快不是volley了。于是第三种写法也被抛弃。

最后一试,第四种写法,这种写法我也不贴了,它确实没有用apache的API,但是,它用了过时的OkUrlFactory,并且运行起来一样不正常,会报迷之错误。于是这种写法也被抛弃。

最后的最后,我要用okhttp的雄心不死,但是只能退而求其次,用okhttp2,我最终用了2.7.5版本。

综上所属,volley对最新的okhttp的支持非常不友好,如果去看volley的源码,它还对HttpClient保留了支持,并且项目内部大量使用了apache的API,这让人感觉非常不爽。如果我是volley的作者,我早就让HttpClient狗带了。而且之前也说了,从更新频率看,volley的维护已经变的相当不积极,作为一个Google官方出品的库,我不知道现在Google对它的态度如何,但我觉得大致上来看,是相当不乐观的,特别是square全家桶横行天下的现在,在网络请求库中,底层支持且仅支持okhttp的Retrofit独领风骚,我觉得Google没有兴趣非要弄一个可以碾压Retrofit的库才肯罢休,何不花时间做点别人没有做过的事情。

我以前喜欢volley的原因还有一个就是因为它可以简单的加载图片,但是功能实在是太有限了,我以前也写过几篇文章,专门写当volley加载图片遇到功能瓶颈的时候怎么解决。在功能上volley被图片加载四大库完全吊打。 

综上所属,volley最终和一些老库一样走向停更是必然的,我不准备再花时间和精力去解决volley的瓶颈,我要向square低头了,我要向Jack Wharton大神低头了,我准备转向Retrofit了,所以,小伙伴们,如果你还在用volley,确实该考虑考虑了。







1 0
原创粉丝点击