Volley框架的基本解读

Posted zero-27

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Volley框架的基本解读相关的知识,希望对你有一定的参考价值。

上一篇Volley框架的基本解读(二)中,我们说到了NetworkDispatcher的run方法,查看源码我们发现,NetworkDispatcher只负责调度并不负责具体的网络请求,它相当于一个中转站,只是将request从RequestQueue手中取来,然后原封不动的交给mNetwork,那么问题来了,mNetwork是什么鬼?我们看源码:


public interface Network 
    /**
     * Performs the specified request.
     * @param request Request to process
     * @return A @link NetworkResponse with data and caching metadata; will never be null
     * @throws VolleyError on errors
     * 
     * 执行request
     */
    public NetworkResponse performRequest(Request<?> request) throws VolleyError;

很显然这是一个接口,里面有一个抽象方法,用于执行请求。问题又来了,这个接口的具体实现是什么?什么时候被创建的?让我们回想一下,当我们调用Volley.newRequestQueue方法时,里面是不是有这么一句话:


// 网络接口的基本实现,处理网络请求或失败重试
        Network network = new BasicNetwork(stack);


随后这个network便以参数的形式传给了RequestQueue,然后RequestQueue又传给NetworkDispatcher,这条线也就清晰了,我们要找的Network的具体实现正是BasicNetwork,发源码证明一下:


public class BasicNetwork implements Network


在看performRequest这个抽象方法的具体实现之前,我们先看看它的构造方法:


public BasicNetwork(HttpStack httpStack) 
        // If a pool isn't passed in, then build a small default pool that will give us a lot of
        // benefit and not use too much memory.
        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
    


HttpStack正是Volley.newRequestQueue中创建传入的,可见这里又创建了ByteArrayPool缓存池,大小为4096,也就是4KB,然后调用了另一个构造方法:


public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) 
        mHttpStack = httpStack;
        mPool = pool;
    

这里没什么好说的,我们来看抽象方法performRequest的具体实现:


@Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError 
    	// 得到开机到现在的时间的毫秒值
        long requestStart = SystemClock.elapsedRealtime();
        while (true) 
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = new HashMap<String, String>();
            try 
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());
                // 真正执行网络请求的其实是mHttpStack
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) // 无修改
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
                            request.getCacheEntry().data, responseHeaders, true);
                

                // Some responses such as 204s do not have content.  We must check.
                // 可能会发生204无内容返回,因此我们必须检查
                if (httpResponse.getEntity() != null) 
                  responseContents = entityToBytes(httpResponse.getEntity());
                 else 
                  // Add 0 byte response as a way of honestly representing a
                  // no-content request.
                  responseContents = new byte[0];
                

                // if the request is slow, log it.
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                // 调试信息
                logSlowRequests(requestLifetime, request, responseContents, statusLine);
                
                if (statusCode < 200 || statusCode > 299) 
                    throw new IOException();
                
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
             catch (SocketTimeoutException e) 
                attemptRetryOnException("socket", request, new TimeoutError());
             catch (ConnectTimeoutException e) 
                attemptRetryOnException("connection", request, new TimeoutError());
             catch (MalformedURLException e) 
                throw new RuntimeException("Bad URL " + request.getUrl(), e);
             catch (IOException e) 
                int statusCode = 0;
                NetworkResponse networkResponse = null;
                if (httpResponse != null) 
                    statusCode = httpResponse.getStatusLine().getStatusCode();
                 else 
                    throw new NoConnectionError(e);
                
                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                if (responseContents != null) 
                    networkResponse = new NetworkResponse(statusCode, responseContents,
                            responseHeaders, false);
                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) 
                        attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));
                     else 
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    
                 else 
                    throw new NetworkError(networkResponse);
                
            
        
    

代码比较长,大家先别晕,因为这里还只是对网络请求返回数据的封装处理而已,还记得我们在NetworkResponse的run方法中,BasicNetwork调用上面这个方法,返回的是什么吗?答案是NetworkResponse,这个类是一个实体类,封装返回数据:


public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
            boolean notModified) 
        this.statusCode = statusCode;
        this.data = data;
        this.headers = headers;
        this.notModified = notModified;
    

封装的参数分别是状态码,返回数据,响应头,无修改标记。


知道了这个后,我们再来分析performRequest,里面同样是一个while死循环,在第12行addCacheHeaders方法用于添加缓存请求头,然后mHttpStack调用同名方法进行网络请求,这里mHttpStack就相当于BasicNetwork的代理,返回HttpResponse,接下来大家应该都很熟悉了,得到状态行,再通过状态行得到状态码,调用convertHeaders方法得到响应头:


/**
     * Converts Headers[] to Map<String, String>.
     * 
     * 获取响应头
     */
    private static Map<String, String> convertHeaders(Header[] headers) 
        Map<String, String> result = new HashMap<String, String>();
        for (int i = 0; i < headers.length; i++) 
            result.put(headers[i].getName(), headers[i].getValue());
        
        return result;
    

之后判断状态码是否是304无修改,作出返回,判断状态码是否是204无内容,否读出内容,是给一个0长度的字节数组,logSlowRequests方法不用管,是调试信息,状态码不正确抛出IOException,经过这些判断之后,封装成NetworkResponse给予返回。


流程走通之后,发现也没有那么难,有木有?


上面说的是成功流程,我们再来看看异常流程,该方法一共捕抓了4个异常,SocketTimeoutException与ConnectTimeoutException调用了attemptRetryOnException方法:


/**
     * Attempts to prepare the request for a retry. If there are no more attempts remaining in the
     * request's retry policy, a timeout exception is thrown.
     * @param request The request to use.
     * 
     * 尝试重试请求,重试策略由RetryPolicy来决定
     */
    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 e) 
            request.addMarker(
                    String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
            throw e;
        
        request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
    

细心的读者可能已经发现这个方法会抛出一个VolleyError异常,而这个异常在performRequest方法中并没有捕抓,同样将它抛了出去,还记得我们再说NetworkDispatcher的run时,里面捕抓了一个VolleyError异常吧,正是这里可能出抛出的。


RetryPolicy是一个重试策略类,它由request创建时,在构造方法中被创建:


public Request(int method, String url, Response.ErrorListener listener) 
        mMethod = method;
        mUrl = url;
        mErrorListener = listener;
        setRetryPolicy(new DefaultRetryPolicy());

        mDefaultTrafficStatsTag = TextUtils.isEmpty(url) ? 0: Uri.parse(url).getHost().hashCode();
    

上面调用了retryPolicy.retry(exception),我们看看DefaultRetryPolicy该类的实现:


/**
     * Prepares for the next retry by applying a backoff to the timeout.
     * @param error The error code of the last attempt.
     * 
     * 这里是重试策略,网络执行BasicNetwork中,网络请求是一个死循环,只有请求成功或抛出异常能够跳出
     */
    @Override
    public void retry(VolleyError error) throws VolleyError 
        mCurrentRetryCount++;
        mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
        // 当前请求次数超过了最大请求次数,就抛出异常,由BasicNetwork再次抛给NetworkDispatcher,
        // 在NetworkDispatcher中做出异常回调
        if (!hasAttemptRemaining()) 
            throw error;
        
    

/**
     * Returns true if this policy has attempts remaining, false otherwise.
     */
    protected boolean hasAttemptRemaining() 
        return mCurrentRetryCount <= mMaxNumRetries;
    

mCurrentRetryCount是当前的请求次数,默认为0,mCurrentTimeoutMs是当前的超时时间,用于调试之用,我们可以无视,mMaxNumRetries是最大的尝试次数,默认为1。


到这里可能已经有人走通了,如果没有,我们再理理思路,retry方法调起,mCurrentRetryCount变为1,hasAttemptRemaining方法返回为true,BasicNetwork类中performRequest方法中while死循环就无法跳出,继续一次请求,如果再次失败,mCurrentRetryCount变为2,hasAttemptRemaining方法返回为false,抛出异常,attemptRetryOnException方法捕抓后,同样抛出,BasicNetwork再次抛出,由NetworkDispatcher来捕抓该异常,执行错误回调。


这错综复杂的线就通了。


我们接着说,MalformedURLException就是指URL错误,直接抛出RuntimeException。


IOException又分几种详细的错误,判断是否返回HttpResponse,否抛出无连接异常,判断数据是否不为空,否抛出网络异常,是封装NetworkResponse,判断401或403错误,是尝试再次请求,否抛出服务器错误。


这里值得一提的是,封装的NetworkResponse就是服务器返回的错误信息,我们可以通过VolleyError.networkResponse.data拿到它。


好了,我们下一篇博客再见。

以上是关于Volley框架的基本解读的主要内容,如果未能解决你的问题,请参考以下文章

Volley框架的基本解读

Volley框架的基本解读

Volley框架的基本解读

Volley框架的基本解读

Volley框架的基本解读

Volley源码解读(上)