使用OKHttp3替换Volley的底层网络请求

来源:互联网 发布:网络歌手灰色天空 编辑:程序博客网 时间:2024/05/17 07:26

一、自定义Volley框架中处理网络请求的HttpStatck,下面我们就使用OKHttp3实现HttpStack的网络请求。
1、在build.gradle文件中添加依赖:

    compile 'com.mcxiaoke.volley:library:1.0.19'    compile 'com.squareup.okio:okio:1.6.0'    compile 'com.squareup.okhttp3:okhttp:3.0.0-RC1'

2、实现volley框架里的HttpStack接口,使用OKHttp3替换Volley的底层网络请求。

package com.bolome.network.request;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 android_ls on 16/1/10. */public class OkHttpStack implements HttpStack {    private final OkHttpClient mClient;    public OkHttpStack(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;    }}

3、创建Volley队列RequestQueue

RequestQueue requestQueue = Volley.newRequestQueue(this, new OkHttpStack(new OkHttpClient()));

二、Volley是Google官方发布的异步网络请求框架,试用场景数据量小,通信频繁的网络操作。Volley 异步网络请求分析:

这里写图片描述

上图是官方给出的Volley架构图,蓝色为主线程,绿色为缓存线程,橙色是网络线程。总的来说,就是一个请求队列和三种线程,UI线程(1个),Cache线程(1个)和Network线程(默认是4个)。

1、UI线程负责添加请求任务,执行任务结果;

2、Cache线程负责检查缓存,命中后直接将任务结果分发到主线程;

3、Network线程由多个任务线程(NetworkDispatcher)组成的,相当于一个大小为size的线程池,这些线程会同时启动,并持续的从任务队列中获取待执行的任务,任务执行完后会将结果分发到UI线程。

4、Request:请求的抽象类。StringRequest、JsonRequest、ImageRequest 都是它的子类,表示某种类型的请求。可扩展性强。

5、RequestQueue.java:请求队列,里面包含一个CacheDispatcher(用于处理走缓存请求的调度线程)、NetworkDispatcher数组(用于处理走网络请求的调度线程),一个ResponseDelivery(返回结果分发接口),通过 start() 方法启动时会启动CacheDispatcher和NetworkDispatchers。然后看创建请求队列方法内的代码:

public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);        String userAgent = "volley/0";        try {            String packageName = context.getPackageName();            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);            userAgent = packageName + "/" + info.versionCode;        } catch (NameNotFoundException e) {        }        if (stack == null) {            if (Build.VERSION.SDK_INT >= 9) {                stack = new HurlStack();            } else {                // Prior to Gingerbread, HttpUrlConnection was unreliable.                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));            }        }        Network network = new BasicNetwork(stack);        RequestQueue queue;        if (maxDiskCacheBytes <= -1)        {            // No maximum size specified            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);        }        else        {            // Disk cache size specified            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);        }        queue.start();        return queue;    }

看到这里,需要了解三个类的作用:
HttpStack.java:处理HTTP请求,返回请求结果。目前Volley中有基于 HttpURLConnection 的 HurlStack 和 基于 Apache HttpClient 的HttpClientStack。

Network.java:调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。

Cache.java:缓存请求结果,Volley默认使用的是基于sdcard缓存的DiskBasedCache。NetworkDispatcher得到请求结果后判断是否需要存储在 Cache,CacheDispatcher会从 Cache 中取缓存结果。

创建Network需要HttpStatck,如果newRequestQueue传入的stack为null,API Level >= 9,采用基于 HttpURLConnection 的 HurlStack;小于 9,采用基于 HttpClient 的 HttpClientStack。

if (stack == null) {    if (Build.VERSION.SDK_INT >= 9) {        stack = new HurlStack();    } else {        // Prior to Gingerbread, HttpUrlConnection was unreliable.        // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html        stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));    }}

接下来启动所需的所有线程:

public void start() {    stop();  // Make sure any currently running dispatchers are stopped.    // Create the cache dispatcher and start it.    mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);    mCacheDispatcher.start();    // Create network dispatchers (and corresponding threads) up to the pool size.    for (int i = 0; i < mDispatchers.length; i++) {        NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,                mCache, mDelivery);        mDispatchers[i] = networkDispatcher;        networkDispatcher.start();    }}

CacheDispatcher.java:继承自Thread,用于调度处理「缓存请求」。启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入NetworkDispatcher去调度处理。

NetworkDispatcher.java:继承自Thread,用于调度处理「网络请求」。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。

ResponseDelivery.java:分发结果的interface,postResponse以及postError。

接下来再回头看一下Volley的架构图。
第一步:把请求加入缓存队列
第二步:「缓存调度线程」CacheDispatcher从缓存队列中取出一个请求,如果缓存命中,就读取缓存响应并解析,然后将结果返回到主线程
第三步:缓存未命中,请求被加入网络请求队列,「网络调度线程」NetworkDispatcher轮询取出请求,HTTP请求传输,解析响应,写入缓存,然后将结果返回到主线程

总结:
在主线程中调用RequestQueue的add()方法来添加一个网络请求任务。
1、以请求方法(GET、POST、PUT…) + URL作为缓存KEY,根据cacheKey从缓存队列里获取该网络请求任务。
a、如果为空的话则把这个任务加入到网络请求队列中;
b、如果不为空的话再判断该缓存是否已过期,如果已经过期了则同样把这个任务 加入到网络请求队列中;
c、否则就认为不需要重发网络请求,直接使用缓存中的数据即可。
2、加入到网络请求队列中的任务
a、发送HTTP请求
b、解析响应结果,写入缓存,并回调主线程。

1 0