Android开源框架之Volley(二)

源码分析

1、入口Volley.newRequestQueue(context),代码如下:
public static RequestQueue newRequestQueue(Context context) {
       return newRequestQueue(context, null);
}

这个方法仅仅只有一行代码,只是调用了newRequestQueue()的方法重载,并给第二个参数传入null。

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
       //获取缓存目录
	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) {}
    //判断HttpStack,默认是null
    if (stack == null) {
        if (Build.VERSION.SDK_INT >= 9) {
            stack = new HurlStack();
        } else {
            // Prior to Gingerbread, HttpUrlConnection was unreliable. 在android2.2之前的版本HttpUrlConnection是不可靠的
            // 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 = new RequestQueue(new DiskBasedCache(cacheDir), network);
    queue.start();

    return queue;
}

<1> 这个方法里先判断如果stack是等于null的,则去创建一个HttpStack对象,这里会判断如果手机系统版本号是大于9的,则创建一个HurlStack的实例,否则就创建一个HttpClientStack的实例。

HurlStack的内部就是使用HttpURLConnection进行网络通讯的。

而HttpClientStack的内部则是使用HttpClient进行网络通讯的。

stack主要是用于执行真正的网络请求。

<2> 创建好了HttpStack之后,接下来又创建了一个Network对象,BasicNetwork中new了一个ByteArrayPool字节缓存池。

public BasicNetwork(HttpStack httpStack) {
    this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}

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

构造这个字节缓存池主要是因为当网络请求得到返回数据,需要在内存开辟出一块区域来存放得到的网络数据,然后拿到解析在UI显示。当客户端频繁的数据请求是,就会每次都要去开辟出一块区域,等不用的时候,GC进行回收,如此GC的负担就相当的重,频繁GC对客户端的性能有直接影响。ByteArrayPool主要就是当需要使内存区域的时候,是先查找缓冲池中有无适合的内存区域,如果没有合适的区域就创建,如果有,直接拿来用,不必每次存数据都要进行内存分配,从而减少内存分配的次数。这样,就会大大减少内存区域堆内存的波动和减少GC的回收。从而提高性能以及处理能力。

<3> new一个RequestQueue对象,并调用它的start()方法进行启动。最后将RequestQueue返回,这样newRequestQueue()的方法就执行结束了。

private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
    
blic RequestQueue(Cache cache, Network network) {
    this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}

public RequestQueue(Cache cache, Network network, int threadPoolSize) {
    this(cache, network, threadPoolSize,
        new ExecutorDelivery(new Handler(Looper.getMainLooper())));//ExecutorDelivery用于处理响应
}

public RequestQueue(Cache cache, Network network, int threadPoolSize,ResponseDelivery delivery) {
    mCache = cache;
    mNetwork = network;
    mDispatchers = new NetworkDispatcher[threadPoolSize];//网络线程调度器,默认开启4个线程
    mDelivery = delivery;
}

ExecutorDelivery继承ResponseDelivery,内部维护了一个Executor,在使用ExecutorDelivery发送响应消息的时候会调用Executor的execute方法

pblic ExecutorDelivery(final Handler handler) {
    // Make an Executor that just wraps the handler.
    mResponsePoster = new Executor() {
        @Override
        public void execute(Runnable command) {
                //使用handler将响应消息切换回UI线程
	        handler.post(command);
        }
    };
}

<4> HttpStack的设计用到的就是策略模式,通过策略模式,在SDK不同的版本时选用不同的策略

2、RequestQueue.start()方法
public void start() {
    this.stop();
    //创建CacheDispatcher缓存调度器实例,然后调用了它的start开启运行
    this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
    this.mCacheDispatcher.start();
    //创建networkDispatcher网络调度器实例,然后调用了它的start开启运行
    for(int i = 0; i < this.mDispatchers.length; ++i) {
        NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
        this.mDispatchers[i] = networkDispatcher;
        networkDispatcher.start();
    }

}

<1> 首先调用stop()方法退出正在运行的所有CacheDispatcher和NetworkDispatcher任务分发器

public void stop() {
    //关闭缓存调度器
    if (mCacheDispatcher != null) {
        mCacheDispatcher.quit();
    }
    //关闭网络调度器
    for (int i = 0; i < mDispatchers.length; i++) {
        if (mDispatchers[i] != null) {
            mDispatchers[i].quit();
        }
    }
}

<2> CacheDispatcher继承Thread,是一个线程,调用start方法就会启用run方法。

<3> NetworkDispatcher继承自Thread的,在RequestQueue的构造方法中知道默认是创建4个网络线程。并分别调用它们的start()方法。就意味着可以并发进行4个请求,大于4个,会排在队列中。这就说明volley适合数据量小的请求

由上面知道当调用了Volley.newRequestQueue(context)之后,默认会有五个线程一直在后台运行,其中1个CacheDispatcher是缓存线程,4个NetworkDispatcher是网络请求线程。

3、创建request请求

经过上面两步后,就创建好了RequestQueue,之后就需要构建出相应的Request,比如StringRequest。

public class StringRequest extends Request<String> {
    private final Listener<String> mListener;

    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        //调用父类
        super(method, url, errorListener);
        mListener = listener;
    }

    /**
     * Creates a new GET request.
     *
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    //监听response
    @Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

    //解析response
    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}

<1> 除了StringRequest之外,volley还提供了ImageRequest、JsonArrayRequest、JsonObjectRequest都继承Request

唯一的区别就是对返回数据的解析方式(parseNetworkResponse)不同,使用的是模板方法模式,对解析方式进行抽象,让子类分别实现。

<2> 在构造方法里面调用的父类的构造方法。进入父类的构造方法:

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();
      }

调用setRetryPolicy方法设置一个默认的DefaultRetryPolicy重试策略,

public DefaultRetryPolicy() {
           
    this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}

DEFAULT_TIMEOUT_MS:连接超时时间,默认是2500

DEFAULT_MAX_RETRIES:最大重试次数,默认是1

DefaultRetryPolicy实现了RetryPolicy接口,是策略模式,是通过调用retry方法来实现重试机制

3、RequestQueue.add()方法

然后调用RequestQueue的add()方法将Request传入就可以完成网络请求操作了。

public Request add(Request request) {
    request.setRequestQueue(this);
    Set var2 = this.mCurrentRequests;
    //将请求添加到mCurrentRequests队列中
	synchronized(this.mCurrentRequests) {
        this.mCurrentRequests.add(request);
    }

    request.setSequence(this.getSequenceNumber());
    request.addMarker("add-to-queue");
    //判断请求是否应该缓存,默认是true
	if (!request.shouldCache()) {
        //将请求添加到mNetworkQueue网络队列中
		this.mNetworkQueue.add(request);
        return request;
    } else {
        Map var7 = this.mWaitingRequests;
        synchronized(this.mWaitingRequests) {
            //获取请求的缓存key,即URL
            String cacheKey = request.getCacheKey();
			//判断mWaitingRequests队列中是否包含cacheKey
            if (this.mWaitingRequests.containsKey(cacheKey)) {
                Queue<Request> stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList();
                }

                ((Queue)stagedRequests).add(request);
                this.mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
                }
            } else {
			    //将请求添加到mCacheQueue缓存队列中
                this.mWaitingRequests.put(cacheKey, (Object)null);
                this.mCacheQueue.add(request);
            }

            return request;
        }
    }
}

<1> add()方法中有4个主要的队列对象:

mCurrentRequests:用来记录所有任务对象,每有一个网络请求,都会加入到这个队列中,而如果完成任务或者取消任务后,会把这个Request移除队列。

mNetworkQueue:网络请求队列,如果有Request对象加入到这个队列则直接处理。

mCacheQueue:缓存队列,加入队列后会检测有无缓存,如果没有缓存或者是过期缓存则转入到mNetworkQueue队列中。

mWaitingRequests:Map类型,其中可以存储若干个Queue<Request<?>队列,用来存储相同请求的Request对象。

<2> 这里用到了一个设计模式—"生产者消费者模式”,生产者负责生产产品放到阻塞队列中,消费者只负责从阻塞队列中获取产品

4、CacheDispatcher.run()方法

在RequestQueue.add()方法中默认将请求添加到缓存队列(mCacheQueue)中。一直在后台等待的缓存线程就要开始运行起来了。

public class CacheDispatcher extends Thread {  

    ……  

    @Override  
    public void run() {  
        if (DEBUG) VolleyLog.v("start new dispatcher");  
		
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
        //初始化本地缓存信息
        mCache.initialize();  
		
        while (true) {  
            try {  
                //从缓存阻塞队列中获取request,没有就阻塞
                final Request<?> request = mCacheQueue.take();  
                request.addMarker("cache-queue-take");  
                // 判断request是否被取消,取消就执行下一个  
                if (request.isCanceled()) {  
                    request.finish("cache-discard-canceled");  
                    continue;  
                }  
                // 根据request从本地缓存中获取缓存对象  
                Cache.Entry entry = mCache.get(request.getCacheKey());  
		        //判断缓存对象是否为空,是就将request添加到网络缓存队列中,然后执行下一个
                if (entry == null) {  
                    request.addMarker("cache-miss");   
                    mNetworkQueue.put(request);  
                    continue;  
                }  
                // 判断缓存对象是否过期,是就将request添加到网络缓存队列中,然后执行下一个  
                if (entry.isExpired()) {  
                    request.addMarker("cache-hit-expired");  
                    request.setCacheEntry(entry);  
                    mNetworkQueue.put(request);  
                    continue;  
                }   
                request.addMarker("cache-hit");  
		        //对数据进行解析
                Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));  
                request.addMarker("cache-hit-parsed");  
				//判断缓存数据是否需要更新,不需要就将数据发送出去
                if (!entry.refreshNeeded()) {  
                    // 将未过期的数据,发送给Response 
                    mDelivery.postResponse(request, response);  
                } else {
				
                    request.addMarker("cache-hit-refresh-needed");  
                    request.setCacheEntry(entry);  
					
                    response.intermediate = true;  
                    //过期就将request添加到网络缓存队列mNetworkQueue
                    mDelivery.postResponse(request, response, new Runnable() {  
                        @Override  
                        public void run() {  
                            try {  
                                mNetworkQueue.put(request);  
                            } catch (InterruptedException e) {  
                                // Not much we can do about this.  
                            }  
                        }  
                    });  
                }  
            } catch (InterruptedException e) {  
                // We may have been interrupted because it was time to quit.  
                if (mQuit) {  
                    return;  
                }  
                continue;  
            }  
        }  
    }  
}

<1> 首先可以看到一个while(true)循环,说明缓存线程始终是在运行的,直到调用quit()方法

<2> 然后会尝试从缓存当中取出响应结果,如何为空的话则把这条请求加入到网络请求队列中,如果不为空的话再判断该缓存是否已过期,如果已经过期了则同样把这条请求加入到网络请求队列中,否则就认为不需要重发网络请求,直接使用缓存中的数据即可。如果需要就将请求加入到网络请求队列中

<3> 再之后就会在调用Request的parseNetworkResponse()方法来对数据进行解析。

<4> 将解析的数据通过mDelivery.postResponse()方法回调给主线程

5、NetworkDispatcher.run()方法。

从上面的分析可以看出,如果request不在缓存队列、已经过期、需要更新,那么就会将request添加到网络请求队列(NetworkQueue)中。

public class NetworkDispatcher extends Thread {  
    …… 
	
    @Override  
    public void run() { 
	
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
        Request<?> request;  
        
		while (true) {  
            try {  
                //从网络缓存队列中获取request,没有就阻塞
                request = mQueue.take();  
            } catch (InterruptedException e) {  
			
                if (mQuit) {  
                    return;  
                }  
                continue;  
            }  
            try {
			
                request.addMarker("network-queue-take"); 						
                // 如果request已经取消,就继续下一个 
                if (request.isCanceled()) {  
                    request.finish("network-discard-cancelled");  
                    continue;  
                }  
                addTrafficStatsTag(request);  
                //执行网络请求  
                NetworkResponse networkResponse = mNetwork.performRequest(request);  
                request.addMarker("network-http-complete");  
                //如果服务器返回304并且已经提交了响应 
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {  
                    request.finish("not-modified");  
                    continue;  
                }  
                //在工作线程中解析数据 
                Response<?> response = request.parseNetworkResponse(networkResponse);  
                request.addMarker("network-parse-complete");  
                //将获取到的数据写入到本地磁盘mCache中  
                if (request.shouldCache() && response.cacheEntry != null) {  
                    mCache.put(request.getCacheKey(), response.cacheEntry);  
                    request.addMarker("network-cache-written");  
                }   
                request.markDelivered();  
                mDelivery.postResponse(request, response);  
            } catch (VolleyError volleyError) {  
                parseAndDeliverNetworkError(request, volleyError);  
            } catch (Exception e) {  
                VolleyLog.e(e, "Unhandled exception %s", e.toString());  
                mDelivery.postError(request, new VolleyError(e));  
            }  
        }  
    }  
} 

<1> 在网络分发器(NetworkDispatcher)中有一个while(true)循环,说明网络请求线程也是在不断运行的。从mNetworkQueue队列中获取request请求

<2> 之后调用mNetwork.performRequest(request)来执行网络请求,这里的mNetwork就是在Volley.newRequestQueue中创建的BasicNetwork对象

<3> 网络请求执行完毕后得到networkResponse,之后就会在调用Request的parseNetworkResponse()方法来对数据进行解析。

<4> 判断request是否需要缓存,是就将数据缓存到磁盘当中

<5> 将解析的数据缓存到本地后通过mDelivery.postResponse()方法回调给主线程

<6> 发生VolleyError或Exception时就会调用mDelivery.postError()方法将错误回调给主线程

private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
    error = request.parseNetworkError(error);
    mDelivery.postError(request, error);
}
6、 BasicNetwork.performRequest(request) 执行网络请求
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
    long requestStart = SystemClock.elapsedRealtime();

    while(true) {
        HttpResponse httpResponse = null;
        byte[] responseContents = null;
        HashMap responseHeaders = new HashMap();

        try {
            Map<String, String> headers = new HashMap();
            this.addCacheHeaders(headers, request.getCacheEntry());
			//调用mHttpStack执行网络操作
            httpResponse = this.mHttpStack.performRequest(request, headers);
            StatusLine statusLine = httpResponse.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            //保存响应头信息
			Map<String, String> responseHeaders = convertHeaders(httpResponse.getAllHeaders());
            if (statusCode == 304) {
                return new NetworkResponse(304, request.getCacheEntry().data, responseHeaders, true);
            }
            //保存响应内容
            byte[] responseContents;
            if (httpResponse.getEntity() != null) {
			    //将响应内容保存到内存中即byte[]数组中
                responseContents = this.entityToBytes(httpResponse.getEntity());
            } else {
                responseContents = new byte[0];
            }

            long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
			//输出log日志
            this.logSlowRequests(requestLifetime, request, responseContents, statusLine);
            
			if (statusCode < 200 || statusCode > 299) {
                throw new IOException();
            }
            //请求成功将response信息封装返回
		    return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
        } catch (SocketTimeoutException var12) {
            //响应超时调用重试策略
			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) {
            int statusCode = false;
            NetworkResponse networkResponse = null;
            if (httpResponse == null) {
                throw new NoConnectionError(var15);
            }

            int statusCode = httpResponse.getStatusLine().getStatusCode();
            VolleyLog.e("Unexpected response code %d for %s", new Object[]{statusCode, request.getUrl()});
            if (responseContents == null) {
                throw new NetworkError(networkResponse);
            }

            networkResponse = new NetworkResponse(statusCode, (byte[])responseContents, responseHeaders, false);
            if (statusCode != 401 && statusCode != 403) {
                throw new ServerError(networkResponse);
            }
            //重试策略
            attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
        }
    }
}

<1> 该方法最主要的就是调用mHttpStack.performRequest执行网络操作,在android系统大于9时mHttpStack是使用HurlStack,否则是HttpClientStack

<2> 调用entityToBytes方法将响应内容保存到byte[]数组中,在上面的分析知道,使用的是ByteArrayPool数组字节池,提高效率。

/** Reads the contents of HttpEntity into a byte[]. */
private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
    PoolingByteArrayOutputStream bytes =
            new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
    byte[] buffer = null;
    try {
	    //获取响应数据
        InputStream in = entity.getContent();
        if (in == null) {
            throw new ServerError();
        }
		//从字节池中获取响应的内存空间
        buffer = mPool.getBuf(1024);
        int count;
		//将数据写入到内存中
        while ((count = in.read(buffer)) != -1) {
            bytes.write(buffer, 0, count);
        }
        return bytes.toByteArray();
    } finally {
        try {
            // Close the InputStream and release the resources by "consuming the content".
            entity.consumeContent();
        } catch (IOException e) {
            // This can happen if there was an exception above that left the entity in
            // an invalid state.
            VolleyLog.v("Error occured when calling consumingContent");
        }
		//释放
        mPool.returnBuf(buffer);
        bytes.close();
    }
}

<3> 重试策略

org.apache.http.conn.ConnectTimeoutException
       
连接HTTP服务端超时或者等待HttpConnectionManager返回可用连接超时,俗称请求超时.

java.net.SocketTimeoutException

Socket通信超时,即从服务端读取数据时超时,俗称响应超时.

Volley就是通过捕捉异常来进行超时重试的.

private static void attemptRetryOnException(String logPrefix, Request<?> request,
        VolleyError exception) throws VolleyError {
    //获取重试实例对象。从上面知道创建request请求的时候,会创建一个默认的重试类DefaultRetryPolicy
	RetryPolicy retryPolicy = request.getRetryPolicy();
    //获取超时时间,即DefaultRetryPolicy默认的超时时间
	int oldTimeout = request.getTimeoutMs();

    try {
	    //调用retry方法重试
        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));
}

@Override
public void retry(VolleyError error) throws VolleyError {
    //当前重试次数加1
	mCurrentRetryCount++;
    mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
    if (!hasAttemptRemaining()) {
        throw error;
    }
}
//判断当前重试次数是否比最大次数大,
protected boolean hasAttemptRemaining() {
    return mCurrentRetryCount <= mMaxNumRetries;
}

从源码可以看得出重试机制就是通过判断重试次数,如果重试次数小于最大次数就不进行处理,这样就不会跳出循环,继续下次请求。如果重试次数大于最大次数就抛出VolleyError异常,跳出循环。BasicNetwork.performRequest这个方法不会处理这个异常,将这个异常继续往上抛出,抛出给NetworkDispatcher.run()方法,刚才在分析NetworkDispatcher.run()方法的时候,知道捕获到这个VolleyError异常的时候会调用mDelivery.postError()方法将错误回调给主线程,这样就完成了整个重试即过程。

从BasicNetwork.performRequest这个方法没有真正的去执行网络请求,调用了mHttpStack.performRequest方法,下面进到mHttpStack.performRequest方法看看

7、mHttpStack.performRequest()

在创建RequestQueue对象的时候知道,手机系统版本号是大于9的,则创建一个HurlStack的实例,否则就创建一个HttpClientStack的实例

这里以HurlStack为例,看一下内部的实现。

HurlStack.performRequest()

public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
    //获取请求Url
	String url = request.getUrl();
    HashMap<String, String> map = new HashMap();
    map.putAll(request.getHeaders());
    map.putAll(additionalHeaders);
    if (this.mUrlRewriter != null) {
        String rewritten = this.mUrlRewriter.rewriteUrl(url);
        if (rewritten == null) {
            throw new IOException("URL blocked by rewriter: " + url);
        }

        url = rewritten;
    }
    //初始化URL和HttpURLConnection
    URL parsedUrl = new URL(url);
    HttpURLConnection connection = this.openConnection(parsedUrl, request);
    Iterator var8 = map.keySet().iterator();
    
    while(var8.hasNext()) {
        String headerName = (String)var8.next();
        connection.addRequestProperty(headerName, (String)map.get(headerName));
    }
    //发起网络请求
    setConnectionParametersForRequest(connection, request);
    ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
    //获取响应码
	int responseCode = connection.getResponseCode();
    if (responseCode == -1) {
        throw new IOException("Could not retrieve response code from HttpUrlConnection.");
    } else {
	    //封装响应的信息
        StatusLine responseStatus = new BasicStatusLine(protocolVersion, connection.getResponseCode(), connection.getResponseMessage());
        BasicHttpResponse response = new BasicHttpResponse(responseStatus);
        response.setEntity(entityFromConnection(connection));
        Iterator var12 = connection.getHeaderFields().entrySet().iterator();

        while(var12.hasNext()) {
            Entry<String, List<String>> header = (Entry)var12.next();
            if (header.getKey() != null) {
                Header h = new BasicHeader((String)header.getKey(), (String)((List)header.getValue()).get(0));
                //添加响应头
				response.addHeader(h);
            }
        }
        
		return response;	
    }
}

<1> 这个方法就可以看到内部是使用HttpURLConnection进行网络请求的。进到openConnection方法

    private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
        HttpURLConnection connection = createConnection(url);
        //获取超时时间,即重试机制中的超时时间
        int timeoutMs = request.getTimeoutMs();
        connection.setConnectTimeout(timeoutMs);
        connection.setReadTimeout(timeoutMs);
        connection.setUseCaches(false);
        connection.setDoInput(true);

        // use caller-provided custom SslSocketFactory, if any, for HTTPS
        if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
            ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
        }

        return connection;
    }

<2> setConnectionParametersForRequest方法

    static void setConnectionParametersForRequest(HttpURLConnection connection,
            Request<?> request) throws IOException, AuthFailureError {
        switch (request.getMethod()) {
            case Method.DEPRECATED_GET_OR_POST:
                // This is the deprecated way that needs to be handled for backwards compatibility.
                // If the request's post body is null, then the assumption is that the request is
                // GET.  Otherwise, it is assumed that the request is a POST.
                byte[] postBody = request.getPostBody();
                if (postBody != null) {
                    // Prepare output. There is no need to set Content-Length explicitly,
                    // since this is handled by HttpURLConnection using the size of the prepared
                    // output stream.
                    connection.setDoOutput(true);
                    connection.setRequestMethod("POST");
                    connection.addRequestProperty(HEADER_CONTENT_TYPE,
                            request.getPostBodyContentType());
                    DataOutputStream out = new DataOutputStream(connection.getOutputStream());
                    out.write(postBody);
                    out.close();
                }
                break;
            case Method.GET:
                // Not necessary to set the request method because connection defaults to GET but
                // being explicit here.
                connection.setRequestMethod("GET");
                break;
            case Method.DELETE:
                connection.setRequestMethod("DELETE");
                break;
            case Method.POST:
                connection.setRequestMethod("POST");
                addBodyIfExists(connection, request);
                break;
            case Method.PUT:
                connection.setRequestMethod("PUT");
                addBodyIfExists(connection, request);
                break;
            default:
                throw new IllegalStateException("Unknown method type.");
        }
    }

判断不同的请求方法GET、POST、PUT等等进入不同的逻辑,设置不同的请求方法,看一下post请求,调用了addBodyIfExists方法

    private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
            throws IOException, AuthFailureError {
        //获取请求的body
		byte[] body = request.getBody();
        if (body != null) {
            connection.setDoOutput(true);
            connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
            //将body写入
			DataOutputStream out = new DataOutputStream(connection.getOutputStream());
            out.write(body);
            out.close();
        }
    }

<3> 请求完毕后就将响应封装并返回。

8、Request.parseNetworkResponse()

从上面的源码分析可知,无论是缓存分发器CacheDispatcher还是网络分发器NetworkDispatcher在执行结束后都会得到NetworkResponse响应对象。之后就会在NetworkDispatcher.run()或CacheDispatcher.run()方法调用Request的parseNetworkResponse(networkResponse)方法来对数据进行解析,这里使用的Request对象就是一开始创建的Request对象。

系统提供的StringRequest/JsonObjectRequest/JsonArrayRequest/ImageRequest或者是自定义的Request。

可以看出如果要自定义Request需要实现parseNetworkResponse。

以StringRequest方法为例,进入源码看一下。

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

将response响应的信息转为String类型,调用Response.success方法。

/** Returns a successful response containing the parsed result. */
public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
    return new Response<T>(result, cacheEntry);
}

将响应的结果封装成Response对象返回,拿到Response对象之后,在NetworkDispatcher.run方法中知道如果请求需要缓存的话就将数据进行缓存,

然后就会调用响应分发器ResponseDelivery.postResponse()方法回调给UI线程

9、 ResponseDelivery.postResponse()

在通过Request.parseNetworkResponse()方法拿到需要的response后,需要将得到的数据返回给主线程更新UI,此时用到ResponseDelivery.postResponse()方法。

由于ResponseDelivery是一个接口,它的实现类是 ExecutorDelivery 。在RequestQueue对象创建的时候创建的。

public class ExecutorDelivery implements ResponseDelivery {
    private final Executor mResponsePoster;

    public ExecutorDelivery(final Handler handler) {
        this.mResponsePoster = new Executor() {
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }

    public ExecutorDelivery(Executor executor) {
        this.mResponsePoster = executor;
    }

    public void postResponse(Request<?> request, Response<?> response) {
        this.postResponse(request, response, (Runnable)null);
    }

    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable));
    }

    public void postError(Request<?> request, VolleyError error) {
        request.addMarker("post-error");
        Response<?> response = Response.error(error);
        this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, (Runnable)null));
    }

    private class ResponseDeliveryRunnable implements Runnable {
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;

        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
            this.mRequest = request;
            this.mResponse = response;
            this.mRunnable = runnable;
        }

        public void run() {
            if (this.mRequest.isCanceled()) {
                this.mRequest.finish("canceled-at-delivery");
            } else {
                if (this.mResponse.isSuccess()) {
                    this.mRequest.deliverResponse(this.mResponse.result);
                } else {
                    this.mRequest.deliverError(this.mResponse.error);
                }

                if (this.mResponse.intermediate) {
                    this.mRequest.addMarker("intermediate-response");
                } else {
                    this.mRequest.finish("done");
                }

                if (this.mRunnable != null) {
                    this.mRunnable.run();
                }

            }
        }
    }
}

<1> 首先可以看到构造函数中有一个handler参数,该构造函数是在new RequestQueue中初始化的

public RequestQueue(Cache cache, Network network, int threadPoolSize) {
    this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}

通过Looper.getMainLooper()创建了一个主线程的handler对象并且创建了一个Executor线程池对象

<2> postResponse方法中又调用了Executor.execute方法执行其中的ResponseDeliveryRunnable对象

<3> 在Runnable的run方法中mRequest.deliverResponse()方法。mRequest.deliverResponse也是Request需要实现的方法

<4> 最后通过handler.post()方法将结果返回给主线程更新UI

10、图片加载

Volley提供了ImageRequest、ImageLoader、NetworkImageView用于加载图片。

ImageLoader内部还是用的ImageRequest,NetworkImageView是对ImageLoader的封装。以ImageLoader的源码为例分析一下

<1> 创建ImageLoader对象

imageLoader = new ImageLoader(mQueue, new BitmapCache());

public ImageLoader(RequestQueue queue, ImageCache imageCache) {
    mRequestQueue = queue;
    mCache = imageCache;
}

public interface ImageCache {
    public Bitmap getBitmap(String url);
    public void putBitmap(String url, Bitmap bitmap);
}

ImageCache是一个接口,在创建ImageLoader需要去实现这个接口。

有两个方法:

getBitmap:从缓存中获取Bitmap对象

putBitmap:将Bitmap保存到缓存对象中

<2> 创建ImageListener对象

ImageListener listener = ImageLoader.getImageListener(imageView,R.drawable.default_image, R.drawable.failed_image);

public static ImageListener getImageListener(final ImageView view,final int defaultImageResId, final int errorImageResId) {
    return new ImageListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            if (errorImageResId != 0) {
                view.setImageResource(errorImageResId);
            }
        }

        @Override
        public void onResponse(ImageContainer response, boolean isImmediate) {
            if (response.getBitmap() != null) {
                view.setImageBitmap(response.getBitmap());
            } else if (defaultImageResId != 0) {
                view.setImageResource(defaultImageResId);
            }
        }
    };
}

getImageListener方法中包含三个参数:

ImageView view :图片显示控件

int defaultImageResId : 占位图片资源

int errorImageResId : 错误图片资源

ImageListener监听中有两个方法:

onErrorResponse:响应出错回调,设置错误图片

onResponse: 响应回调,如果图片为空,设置占位图,不为空,显示图片

<3> 调用ImageLoader的get()方法加载网络上的图片

imageLoader.get(“https://img-my.csdn.net/uploads/201404/13/1397393290_5765.jpeg”, listener);

public ImageContainer get(String requestUrl, final ImageListener listener) {
    return get(requestUrl, listener, 0, 0);
}	

public ImageContainer get(String requestUrl, ImageListener imageListener,int maxWidth, int maxHeight) {
    //不是在主线程就抛出异常
    throwIfNotOnMainThread();
    //获取缓存key
    final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);

    //从缓存中获取图片
    Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
	//图片是否为空
    if (cachedBitmap != null) {
        // 不为空就调用imageListener.onResponse响应
        ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
        imageListener.onResponse(container, true);
        return container;
    }
    
    //创建ImageContainer对象
    ImageContainer imageContainer =
            new ImageContainer(null, requestUrl, cacheKey, imageListener);

    // 通知imageListener设置占位图
    imageListener.onResponse(imageContainer, true);

    // Check to see if a request is already in-flight.
    BatchedImageRequest request = mInFlightRequests.get(cacheKey);
    if (request != null) {
        // If it is, add this request to the list of listeners.
        request.addContainer(imageContainer);
        return imageContainer;
    }

    // 创建ImageRequest对象
    Request<?> newRequest =
        new ImageRequest(requestUrl, new Listener<Bitmap>() {
            @Override
            public void onResponse(Bitmap response) {
                onGetImageSuccess(cacheKey, response);
            }
        }, maxWidth, maxHeight,
        Config.RGB_565, new ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                onGetImageError(cacheKey, error);
            }
        });
    //将请求添加到mRequestQueue
    mRequestQueue.add(newRequest);
    mInFlightRequests.put(cacheKey,
            new BatchedImageRequest(newRequest, imageContainer));
    return imageContainer;
}	

如果不设置图片的宽高,默认就是0

获取缓存key 调用getCacheKey方法

private static String getCacheKey(String url, int maxWidth, int maxHeight) {
    return new StringBuilder(url.length() + 12).append("#W").append(maxWidth)
        .append("#H").append(maxHeight).append(url).toString();
}	

使用的是url和图片的宽高作为key

如果缓存中没有图片图片的话,就创建ImageRequest对象。

public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
        Config decodeConfig, Response.ErrorListener errorListener) {
	
    super(Method.GET, url, errorListener);
    setRetryPolicy(
            new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));
    mListener = listener;
    mDecodeConfig = decodeConfig;
    mMaxWidth = maxWidth;
    mMaxHeight = maxHeight;
}

这里看到图片加载的重试机制默认值和其他request不同。

IMAGE_TIMEOUT_MS : 超时默认是1000
IMAGE_MAX_RETRIES : 最大重试次数是2

public Priority getPriority() {
    return Priority.LOW;
}

优先级设置为Low,其他都是NORMAL

在网络获取到图片信息后就会调用ImageRequest.parseNetworkResponse去解析图片信息

protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {
    // Serialize all decode on a global lock to reduce concurrent heap usage.
    synchronized (sDecodeLock) {
        try {
		    //解析响应信息
            return doParse(response);
        } catch (OutOfMemoryError e) {
            VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl());
            return Response.error(new ParseError(e));
        }
    }
}

private Response<Bitmap> doParse(NetworkResponse response) {
    byte[] data = response.data;
    BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
    Bitmap bitmap = null;
    if (mMaxWidth == 0 && mMaxHeight == 0) {
        decodeOptions.inPreferredConfig = mDecodeConfig;
        bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
    } else {
        // If we have to resize this image, first get the natural bounds.
        decodeOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
        int actualWidth = decodeOptions.outWidth;
        int actualHeight = decodeOptions.outHeight;

        // Then compute the dimensions we would ideally like to decode to.
        int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
                actualWidth, actualHeight);
        int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
                actualHeight, actualWidth);

        // Decode to the nearest power of two scaling factor.
        decodeOptions.inJustDecodeBounds = false;
        // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?
        // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
        decodeOptions.inSampleSize =
            findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
        Bitmap tempBitmap =
            BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);

        // If necessary, scale down to the maximal acceptable size.
        if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
                tempBitmap.getHeight() > desiredHeight)) {
            bitmap = Bitmap.createScaledBitmap(tempBitmap,
                    desiredWidth, desiredHeight, true);
            tempBitmap.recycle();
        } else {
            bitmap = tempBitmap;
        }
    }

    if (bitmap == null) {
        return Response.error(new ParseError(response));
    } else {
        return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
    }
}	

如果不设置图片的宽高,就直接返回。如果设置了图片的宽高信息,就对图片进行压缩处理

处理完成之后就会Response.success或error创建Response对象,通过上面分析最终会回调给 ImageRequest的deliverResponse方法

@Override
protected void deliverResponse(Bitmap response) {
    //这个mListener就是在ImageLoader中创建ImageRequest创建的Listener
    mListener.onResponse(response);
}

// 创建ImageRequest对象
Request<?> newRequest =
    new ImageRequest(requestUrl, new Listener<Bitmap>() {
        @Override
        public void onResponse(Bitmap response) {
            onGetImageSuccess(cacheKey, response);
        }
    }, maxWidth, maxHeight,
    Config.RGB_565, new ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            onGetImageError(cacheKey, error);
        }
});

private void onGetImageSuccess(String cacheKey, Bitmap response) {
    // 将图片放到缓存中
    mCache.putBitmap(cacheKey, response);

    BatchedImageRequest request = mInFlightRequests.remove(cacheKey);

    if (request != null) {
        // Update the response bitmap.
        request.mResponseBitmap = response;

        // 发送图片信息
        batchResponse(cacheKey, request);
    }
}

private void batchResponse(String cacheKey, BatchedImageRequest request) {
    mBatchedResponses.put(cacheKey, request);

    if (mRunnable == null) {
        mRunnable = new Runnable() {
            @Override
            public void run() {
                for (BatchedImageRequest bir : mBatchedResponses.values()) {
                    for (ImageContainer container : bir.mContainers) {
                        // If one of the callers in the batched request canceled the request
                        // after the response was received but before it was delivered,
                        // skip them.
                        if (container.mListener == null) {
                            continue;
                        }
						//判断是否有错误
                        if (bir.getError() == null) {
                            container.mBitmap = bir.mResponseBitmap;
                            container.mListener.onResponse(container, false);
                        } else {
                            container.mListener.onErrorResponse(bir.getError());
                        }
                    }
                }
                mBatchedResponses.clear();
                mRunnable = null;
            }

        };
        // Post the runnable.
        mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);
    }
}

判断是否有错误,没有的话就调用container.mListener.onResponse即ImageListener.onResponse方法去显示图片,否则就container.mListener.onErrorResponse方法显示错误图片

至此整个volley的源码的分析就梳理结束了。

volley 请求重复问题

方案一 对Request设置重试策略时,更改默认超时时间

request.setRetryPolicy( new DefaultRetryPolicy( 500000,//默认超时时间,应设置一个稍微大点儿的,例如本处的500000 
                             DefaultRetryPolicy.DEFAULT_MAX_RETRIES,//默认最大尝试次数 
                             DefaultRetryPolicy.DEFAULT_BACKOFF_MULT ) );

方案二 在HurlStack中的openConnection方法中,在相应段落增加

connection.setChunkedStreamingMode(0)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值