1. 程式人生 > >Volley用OkHttp3作為底部的框架的一個解決方案

Volley用OkHttp3作為底部的框架的一個解決方案

Android6.0刪除了一些org.apache.http包中的一些類,所以網上的一些解決方案就會有一點不太合適。下面是我自己寫了一個解決方案。

1,首先要匯入相應的包:

    compile 'com.mcxiaoke.volley:library:1.0.19'
    compile 'com.squareup.okhttp3:okhttp:3.0.1',

2,重寫Volley的Volley類,支援OkHttp3

package com.yyw.volleydemo.volley.utils;

import android.content.Context;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.DiskBasedCache;

import java.io.File;

import okhttp3.OkHttpClient;

/**
 * Created by yyw on 2016/1/29.
 * 重寫Volley的Volley類,支援OkHttp3
 */
public class Volley3 {
    public Volley3() {
    }

    /**
     * 建立一個客戶端的請求佇列
     * @param context Context
     * @param stack OkHttpStack
     * @return
     */
    public static RequestQueue newRequestQueue(Context context, OkHttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), "volley");//快取地址:/data/data/com.yyw.volleydemo/cache/volley
        if(stack == null) {//如果請求的stack為空就建一個預設的
            stack = new OkHttpStackImpl(new OkHttpClient.Builder().build());
        }
<span style="white-space:pre">	</span>//一個自定義的類,實現NetWork介面</span>
        BasicNetwork3 network1 = new BasicNetwork3(stack);
        RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
        queue1.start();
        return queue1;
    }

    /**
     * 建立一個指定請求客戶端的請求佇列
     * @param context 上下文
     * @param client OkHttpClient
     * @return 請求佇列
     */
    public static RequestQueue newRequestQueueByClient(Context context, OkHttpClient client) {
        return newRequestQueue(context, new OkHttpStackImpl(client));
    }
    /**
     * 建立一個預設請求客戶端的請求佇列
     * @param context 上下文
     * @return
     */
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);
    }
}

3,自定義的一個NetWork 模仿BasicNetwork只是把HttpStack,Response換成我們自定義的或Okhttp3中的

package com.yyw.volleydemo.volley.utils;

import android.os.SystemClock;

import com.android.volley.AuthFailureError;
import com.android.volley.Cache;
import com.android.volley.Network;
import com.android.volley.NetworkError;
import com.android.volley.NetworkResponse;
import com.android.volley.NoConnectionError;
import com.android.volley.Request;
import com.android.volley.RetryPolicy;
import com.android.volley.ServerError;
import com.android.volley.TimeoutError;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.ByteArrayPool;

import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.cookie.DateUtils;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import okhttp3.Headers;
import okhttp3.Response;

/**
 * Created by yyw on 2016/1/29.
 * 自定義的一個NetWork
 * 模仿BasicNetwork只是把HttpStack,Response換成我們自定義的或Okhttp3中的
 */
public class BasicNetwork3 implements Network {
    protected static final boolean DEBUG;
    private static int SLOW_REQUEST_THRESHOLD_MS;
    private static int DEFAULT_POOL_SIZE;
    protected final OkHttpStack mHttpStack;
    protected final ByteArrayPool mPool;

    static {
        DEBUG = VolleyLog.DEBUG;
        SLOW_REQUEST_THRESHOLD_MS = 3000;
        DEFAULT_POOL_SIZE = 4096;
    }

    public BasicNetwork3(OkHttpStack httpStack) {
        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
    }

    public BasicNetwork3(OkHttpStack httpStack, ByteArrayPool pool) {
        this.mHttpStack = httpStack;
        this.mPool = pool;
    }

    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();

        while(true) {
            Response okHttpResponse = null;
            Object responseContents = null;
            HashMap responseHeaders = new HashMap();

            try {
                HashMap e = new HashMap();
                this.addCacheHeaders(e, request.getCacheEntry());
                okHttpResponse = this.mHttpStack.performRequest(request, e);


                int networkResponse1 = okHttpResponse.code();
                Map responseHeaders1 = convertHeaders(okHttpResponse.headers());
                if(networkResponse1 == 304) {
                    return new NetworkResponse(304, request.getCacheEntry().data, responseHeaders1, true);
                }

                byte[] responseContents1 = okHttpResponse.body().bytes();
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                this.logSlowRequests(requestLifetime, request, responseContents1, networkResponse1);
                if(networkResponse1 != 200 && networkResponse1 != 204) {
                    throw new IOException();
                }

                return new NetworkResponse(networkResponse1, responseContents1, responseHeaders1, false);
            } catch (SocketTimeoutException var12) {
                var12.printStackTrace();
                attemptRetryOnException("socket", request, new TimeoutError());
            } catch (ConnectTimeoutException var13) {
                attemptRetryOnException("connection", request, new TimeoutError());
            } catch (MalformedURLException var14) {
                throw new RuntimeException("Bad URL " + request.getUrl(), var14);
            } catch (IOException var15) {
                NetworkResponse networkResponse = null;
                if(okHttpResponse == null) {
                    throw new NoConnectionError(var15);
                }

                int statusCode1 = okHttpResponse.code();
                VolleyLog.e("Unexpected response code %d for %s", new Object[]{Integer.valueOf(statusCode1), request.getUrl()});
                if(responseContents == null) {
                    throw new NetworkError(networkResponse);
                }

                networkResponse = new NetworkResponse(statusCode1, (byte[])responseContents, responseHeaders, false);
                if(statusCode1 != 401 && statusCode1 != 403) {
                    throw new ServerError(networkResponse);
                }

                attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
            }
        }
    }

    private void logSlowRequests(long requestLifetime, Request<?> request, byte[] responseContents, int  status) {
        if(DEBUG || requestLifetime > (long)SLOW_REQUEST_THRESHOLD_MS) {
            VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], [rc=%d], [retryCount=%s]", new Object[]{request, Long.valueOf(requestLifetime), responseContents != null?Integer.valueOf(responseContents.length):"null", Integer.valueOf(status), Integer.valueOf(request.getRetryPolicy().getCurrentRetryCount())});
        }

    }

    private static void attemptRetryOnException(String logPrefix, Request<?> request, VolleyError exception) throws VolleyError {
        RetryPolicy retryPolicy = request.getRetryPolicy();
        int oldTimeout = request.getTimeoutMs();

        try {
            retryPolicy.retry(exception);
        } catch (VolleyError var6) {
            request.addMarker(String.format("%s-timeout-giveup [timeout=%s]", new Object[]{logPrefix, Integer.valueOf(oldTimeout)}));
            throw var6;
        }

        request.addMarker(String.format("%s-retry [timeout=%s]", new Object[]{logPrefix, Integer.valueOf(oldTimeout)}));
    }

    private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
        if(entry != null) {
            if(entry.etag != null) {
                headers.put("If-None-Match", entry.etag);
            }

            if(entry.serverDate > 0L) {
                Date refTime = new Date(entry.serverDate);
                headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
            }

        }
    }

    private static Map<String, String> convertHeaders(Headers headers) {
        HashMap result = new HashMap();

        for(int i = 0; i < headers.size(); ++i) {
            result.put(headers.name(i), headers.value(i));
        }

        return result;
    }
}

4,自定義的Stack介面

package com.yyw.volleydemo.volley.utils;


import com.android.volley.AuthFailureError;
import com.android.volley.Request;

import java.io.IOException;
import java.util.Map;

import okhttp3.Response;

/**
 * Created by yyw on 2016/1/28.
 * 自定義的Stack介面
 */
public interface OkHttpStack  {
    /**
     * 通過這個方法來去執行請求
     * @param var1  volley中的請求
     * @param var2  引數
     * @return OkHttp3的請求響應
     * @throws IOException
     * @throws AuthFailureError
     */
    Response performRequest(Request<?> var1, Map<String, String> var2) throws IOException, AuthFailureError;
}

5,提供預設的OkHttpStack實現類(參考使用OKHttp3替換Volley的底層網路請求

package com.yyw.volleydemo.volley.utils;

import com.android.volley.AuthFailureError;
import com.android.volley.Request;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * Created by yyw on 2016/1/29.
 * 預設的請求Stack例項
 */
public class OkHttpStackImpl implements OkHttpStack{
    private final OkHttpClient mClient;//提供的可以克隆的OkHttpClient,例如HTTPS請求
    public OkHttpStackImpl(OkHttpClient client) {
        this.mClient = client;
    }

    @SuppressWarnings("deprecation")
    private  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.");
        }
    }

    /**
     * 如果需要對OkHttp的請求體進行修改或者新增一些內容可以在這個地方修改
     * @param request
     * @return
     * @throws AuthFailureError
     */
    protected  RequestBody createRequestBody(Request request) throws AuthFailureError {
        final byte[] body = request.getBody();
        if (body == null) return null;

        return RequestBody.create(MediaType.parse(request.getBodyContentType()), body);
    }

    @Override
    public Response 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();

        return okHttpResponse;
    }
}
好了在建立RequstQueue的時候只要呼叫Volley3的方法就可以了。

程式碼地址