volley和底層使用okhttp3的框架的整合嘗試

搜索了經常使用的http請求框架之後,思索:java

一、對於HttpClient, Apache HttpClient早就不推薦httpclient,5.0以後乾脆廢棄,後續會刪除。6.0刪除了HttpClient。

二、HttpURLConnection 和okHttp想對比,okhttp更強大支持大文件傳輸,而對於HttpUrlConnection和volley(底層也是封裝的HttpUrlConnetion)老是有些力不從心。

三、對於Volley框架,中止了更新,而OkkHttp獲得了官方的承認,並在不斷優化。

四、對於okHttp3框架由於封裝的callback沒有volley框架請求隊列方便,故而萌生想法,將volley的底層HttpUrlConnection更改成Okhttp。

Android6.0刪除了一些org.apache.http包中的一些類,網上的一些解決方案有些不太合適宜。

如下附代碼:android

重寫Volley方法,使得和原來volley的請求隊列基於一致:apache

package com.android.volley.oktoolbox;

import android.content.Context;

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

import java.io.File;

import okhttp3.OkHttpClient;

/**
 * Created by Administrator on 2018-03-13.
 */

public class OkVelley {

    public OkVelley() {
    }

    /**
     * 建立一個客戶端的請求隊列
     * @param context Context
     * @param stack OkHttpStack
     * @return
     */
    public static RequestQueue newRequestQueue(Context context, OkHttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), "volley");//緩存地址:/data/data/包名/cache/volley
        if(stack == null) {//若是請求的stack爲空就建一個默認的
            stack = new OkHttpStackImpl(new OkHttpClient.Builder().build());
        }
//一個自定義的類,實現NetWork接口</span>
                OkBasicNetwork okNetwork = new OkBasicNetwork(stack);
        RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), okNetwork);
        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);
    }
}

重寫BasicNetwork緩存

package com.android.volley.oktoolbox;

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 Administrator on 2018-03-13.
 */

public class OkBasicNetwork 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 OkBasicNetwork(OkHttpStack httpStack) {
        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
    }

    public OkBasicNetwork(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;
    }
}
package com.android.volley.oktoolbox;

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


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

import okhttp3.Response;

/**
 * Created by Administrator on 2018-03-12.
 */

public interface OkHttpStack {

     Response performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError;
}
package com.android.volley.oktoolbox;

import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.zhy.http.okhttp.builder.OkHttpRequestBuilder;
import com.zhy.http.okhttp.request.OkHttpRequest;

import org.apache.http.HttpResponse;

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

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

/**
 * Created by Administrator on 2018-03-12.
 */

public class OkHttpStackImpl implements OkHttpStack {
    private OkHttpClient mClient;

    public OkHttpStackImpl(OkHttpClient client){
        this.mClient = client;
    }

    public OkHttpStackImpl(){
        this(new OkHttpClient());
    }

    /**
     *
     * @param builder okhttp的builder去建立請求
     * @param request volley的請求
     */
    public void setConnectionParameterForRequest(okhttp3.Request.Builder builder,Request<?> request) throws AuthFailureError {
        switch (request.getMethod()){
            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.PATCH:
                builder.patch(createRequestBody(request));
                break;
            case Request.Method.DEPRECATED_GET_OR_POST:
                byte[] body = request.getBody();
                if(body!=null){
                    builder.post(RequestBody.create(MediaType.parse(request.getBodyContentType()),body));
                }
                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;
            default:
                throw new RuntimeException("unkonwn method type");

        }
    }

    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 timeMs = request.getTimeoutMs();
        OkHttpClient client = mClient.newBuilder().readTimeout(timeMs, TimeUnit.SECONDS).writeTimeout(timeMs,TimeUnit.SECONDS)
                .connectTimeout(timeMs,TimeUnit.SECONDS).build();
        okhttp3.Request.Builder builder = new okhttp3.Request.Builder();
        Map<String,String> headers = request.getHeaders();
        for(final String name:headers.keySet()){
            builder.addHeader(name,headers.get(name));
        }
        for(final String name:additionalHeaders.keySet()){
            builder.addHeader(name,additionalHeaders.get(name));
        }
        setConnectionParameterForRequest(builder,request);
        okhttp3.Request okHttpRequest = builder.url(request.getUrl()).build();
        okhttp3.Response response = client.newCall(okHttpRequest).execute();
        return response;
    }
}

最後在Volley源碼中RequestManager替換成本身的Volley,就能夠,親測okcookie

相關文章
相關標籤/搜索