Android知识

VOLLEY源码完全解析之Request

2018-01-28  本文已影响0人  抽象语法树

从StringRequest,JsonObjectRequest,JsonArrayRequest说起

        mStringRequest = new StringRequest(Request.Method.GET, url, vl.getListener(), vl.getErrorListener());
        mStringRequest.setTag(tag);
        MyApplication.getRequestQueue().add(mStringRequest);

即创建一个StringRequest,指定HTTP协议的类型以及响应成功和失败的监听事件。
而点进去查看StringRequest的源码可以发现,StringRequest继承自抽象类Request,实现了抽象方法deliverResponse和parseNetworkResponse即分发监听事件的方法和解析数据的方法。

    protected void deliverResponse(String response) {
        this.mListener.onResponse(response);
    }

    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException var4) {
            parsed = new String(response.data);
        }

        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}

我们知道,HTTP响应的信息由状态行、消息报头、响应正文三部分组成,而parseNetworkResponse方法中将响应正文按一定的编码格式(根据response.headers的内容,若response.headers失败,则使用android默认的DEFAULT_CHARSET,各API版本不同,大部分为UTF-8)进行解析后返回。同理可知,JsonObjectRequest,JsonArrayRequest与StringRequest的区别恐怕是出在parseNetworkResponse方法上:

    protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers, "utf-8"));
            return Response.success(new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException var3) {
            return Response.error(new ParseError(var3));
        } catch (JSONException var4) {
            return Response.error(new ParseError(var4));
        }
  protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers, "utf-8"));
            return Response.success(new JSONArray(jsonString), HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException var3) {
            return Response.error(new ParseError(var3));
        } catch (JSONException var4) {
            return Response.error(new ParseError(var4));
        }
    }

可以看到,JsonObjectRequest和JsonArrayRequest只不过是将响应内容中的数据解析成JsonObject或者JsonArray后返回,不过需要注意的是这个类都继承在JsonRequest之下,而JsonRequest则对Request类进行了进一步的封装,这里先不讨论。
通过上面对三个请求类的分析,我们可以发现,请求子类中,并没有对HTTP响应失败的处理,自然而然,错误信息的处理肯定放在了Request中,带着这个问题,继续观察Request类。

Request类解析

    private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
    private final MarkerLog mEventLog;
    private final int mMethod;
    private final String mUrl;
    private String mRedirectUrl;
    private final int mDefaultTrafficStatsTag;
    private final ErrorListener mErrorListener;
    private Integer mSequence;
    private RequestQueue mRequestQueue;
    private boolean mShouldCache;
    private boolean mCanceled;
    private boolean mResponseDelivered;
    private long mRequestBirthTime;
    private static final long SLOW_REQUEST_THRESHOLD_MS = 3000L;
    private RetryPolicy mRetryPolicy;
    private Entry mCacheEntry;
    private Object mTag;

DEFAULT_PARAMS_ENCODING :对于传递的参数的默认编码方式。
MarkerLog:DEBUG工具,同于追踪该Request的生命周期。
mMethod:HTTP的请求方式,支持以下几种:

        int DEPRECATED_GET_OR_POST = -1;
        int GET = 0;
        int POST = 1;
        int PUT = 2;
        int DELETE = 3;
        int HEAD = 4;
        int OPTIONS = 5;
        int TRACE = 6;
        int PATCH = 7;

mUrl:网络请求的URL。
mRedirectUrl:状态码为3xx使用的重定向URL。
mDefaultTrafficStatsTag:Default tag。主机的hashCode。
mErrorListener:请求出现错误的监听事件。
mSequence:请求的序列号,在重试策略或者请求队列的调度中可能用到?
mShouldCache:是否需要缓存。
mCanceled:是否被取消。
mResponseDelivered:是否分发响应。
mRequestBirthTime:请求产生的时间,用于跟踪请求,从而抛弃慢的请求 。
SLOW_REQUEST_THRESHOLD_MS :慢请求的阈值。
mRetryPolicy:重试策略。
mCacheEntry:缓存记录。
mTag:一个标记,批量取消任务的时候可能用到。

    public Request(int method, String url, ErrorListener listener) {
        this.mEventLog = MarkerLog.ENABLED?new MarkerLog():null;
        this.mShouldCache = true;
        this.mCanceled = false;
        this.mResponseDelivered = false;
        this.mRequestBirthTime = 0L;
        this.mCacheEntry = null;
        this.mMethod = method;
        this.mUrl = url;
        this.mErrorListener = listener;
        this.setRetryPolicy(new DefaultRetryPolicy());
        this.mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
    }

可以看到,在构造函数中,除了对传入的method,url,listener进行赋值外(响应成功的listener放在其子类中,具有更好的可扩展性),还对其他的成员变量进行默认的赋值。其中需要注意的是setRetryPolicy方法,观察其默认的重试策略。
首先,所有的重试策略都需要实现RetryPolicy接口,也就意味着我们可以实现自己的重试策略。

public interface RetryPolicy {
    int getCurrentTimeout();

    int getCurrentRetryCount();

    void retry(VolleyError var1) throws VolleyError;
}

其中getCurrentTimeout和getCurrentRetryCount分别为当前超时时间和重试次数,而retry方法则是具体的重试手段。在VolleyError封装了发生错误的响应信息,意味着我们可以拿到具体的错误代码。
当重试操作不能执行的时候,我们需要抛出VolleyError异常,从而结束该请求(此操作在BasicNetwork中实现,后面会继续分析)。
而VOLLEY的默认重试策略为:

public class DefaultRetryPolicy implements RetryPolicy {
    private int mCurrentTimeoutMs;
    private int mCurrentRetryCount;
    private final int mMaxNumRetries;
    private final float mBackoffMultiplier;
    public static final int DEFAULT_TIMEOUT_MS = 2500;
    public static final int DEFAULT_MAX_RETRIES = 1;
    public static final float DEFAULT_BACKOFF_MULT = 1.0F;

    public DefaultRetryPolicy() {
        this(2500, 1, 1.0F);
    }

    public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
        this.mCurrentTimeoutMs = initialTimeoutMs;
        this.mMaxNumRetries = maxNumRetries;
        this.mBackoffMultiplier = backoffMultiplier;
    }

    public int getCurrentTimeout() {
        return this.mCurrentTimeoutMs;
    }

    public int getCurrentRetryCount() {
        return this.mCurrentRetryCount;
    }

    public float getBackoffMultiplier() {
        return this.mBackoffMultiplier;
    }

    public void retry(VolleyError error) throws VolleyError {
        ++this.mCurrentRetryCount;
        this.mCurrentTimeoutMs = (int)((float)this.mCurrentTimeoutMs + (float)this.mCurrentTimeoutMs * this.mBackoffMultiplier);
        if(!this.hasAttemptRemaining()) {
            throw error;
        }
    }

    protected boolean hasAttemptRemaining() {
        return this.mCurrentRetryCount <= this.mMaxNumRetries;
    }
}

可以看到,其默认超时时间为2500ms,重试次数为1,延时增量为1。其具体实现的策略很简单,每次将重试次数加1,每次重试的超时时长均为默认值,重试一次后仍失败则抛出异常,结束该次请求。

回到Request类,从源码中可以看到,Request中许多方法都是对自身变量的存取操作,所以只对一些有价值的方法进行分析。
看到finish()方法:

    void finish(final String tag) {
        if(this.mRequestQueue != null) {
            this.mRequestQueue.finish(this);
        }

        final long threadId;
        if(MarkerLog.ENABLED) {
            threadId = Thread.currentThread().getId();
            if(Looper.myLooper() != Looper.getMainLooper()) {
                Handler mainThread = new Handler(Looper.getMainLooper());
                mainThread.post(new Runnable() {
                    public void run() {
                        Request.this.mEventLog.add(tag, threadId);
                        Request.this.mEventLog.finish(this.toString());
                    }
                });
                return;
            }

            this.mEventLog.add(tag, threadId);
            this.mEventLog.finish(this.toString());
        } else {
            threadId = SystemClock.elapsedRealtime() - this.mRequestBirthTime;
            if(threadId >= 3000L) {
                VolleyLog.d("%d ms: %s", new Object[]{Long.valueOf(threadId), this.toString()});
            }
        }

    }

可以看到,该方法主要是调用RequestQueue的finish()方法来结束请求的,然后根据DEBUG日志的设置进行打印相关信息,这里先不对Volley的DEBUG结构进行解释。

再看到它的encodeParameters()函数:

    private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
        StringBuilder encodedParams = new StringBuilder();
        try {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
                encodedParams.append('=');
                encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
                encodedParams.append('&');
            }
            return encodedParams.toString().getBytes(paramsEncoding);
        } catch (UnsupportedEncodingException uee) {
            throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
        }
    }

其主要作用是解析传递过来的参数,将其拼接并转换成byte数组。但可以发现,在Request内部都是通过getParams()作为参数传递给这个方法的,再来看到getParams():

    protected Map<String, String> getParams() throws AuthFailureError {
        return null;
    }

看到这里想到了什么?没错,我们在使用POST方式请求时,都需要重写getParams()方法,建造一个map来放置我们的参数就是因为这里默认返回空。那么这里为什么不将getParams()写成抽象函数呢?因为Request有多种请求方式,并不是每种请求方式都需要传递参数的,所以写成抽象方法的形式对不需要传参的请求方式就不大友好了,所以这里写成了可重写的protected方法。

小结

上一篇 下一篇

猜你喜欢

热点阅读