首页 > 程序开发 > 移动开发 > Android >

Android Volley小白解析(一)

2016-07-12

做安卓一年有余,意识到网络请求框架算是很重要的一块,考虑到Volley是谷歌自带的,决定好好研究研究源码,去理理逻辑思路。首先呢,Volley去哪里获取,看下图即可,在安卓源码的frameworks目录下,然后导入到ecl

做安卓一年有余,意识到网络请求框架算是很重要的一块,考虑到Volley是谷歌自带的,决定好好研究研究源码,去理理逻辑思路

首先呢,Volley去哪里获取,看下图即可,在安卓源码的frameworks目录下,然后导入到eclipse中即可去研究了

\

使用Volley的第一步,首先要调用Volley.newRequestQueue(context)方法来获取一个RequestQueue对象,那么我们自然要从这个方法开始看起了,代码如下所示

    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) {
        }

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // 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、创建缓存目录

File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

getCacheDir()方法用于获取/data/data//cache目录,创建volley的目录,用来做后续的缓存目录

2、创建对应对应版本的HttpStack实例

if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
上面这段代码是Build.VERSION.SDK_INT是获取当前手机版本,则创建一个HurlStack的实例,否则就创建一个HttpClientStack的实例。实际上HurlStack的内部就是使用HttpURLConnection进行网络通讯的,而HttpClientStack的内部则是使用HttpClient进行网络通讯的
3、Network network = new BasicNetwork(stack);生成自定义的Network对象,看看这个网络对象的构造函数

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

    public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
        mHttpStack = httpStack;
        mPool = pool;
    }
构造函数着重看new ByteArrayPool(DEFAULT_POOL_SIZE),那么ByteArrayPool这个类做了什么操作?

在对响应的实体进行操作的时候,使用到了byte[] ,由于volley是轻量级频次高的网络请求框架,因此会大量使用到byte[] ,这样的话会频繁创建和销毁byte[]。为了提高性能,volley定义了一个byte[]缓冲池,即ByteArrayPool 。

在ByteArrayPool 内,定义了 两个集合,

  private List mBuffersByLastUse = new LinkedList();
    private List mBuffersBySize = new ArrayList(64);

分别是存储按按使用先后顺序排列byte[]的list和大小顺序排列byte[]的list 。在volley中所需要使用到的byte[]从该缓冲池中来取,当byte[]使用完毕后再归还到该缓冲池,从而避免频繁的创建和销毁byte[]。

看下ByteArrayPool的如下方法:

getBuf:从池中获取一个可用的byte[],如果没有,就创建一个。参数为想要获取多大长度的byte[]

returnBuf:当使用完一个byte[]后,将该byte[]返回到池中

trim:当现有字节总数超过了设定的界限,那么需要清理

4、创建RequestQueue对象,在RequestQueue构造方法中,进行如下初始化操作

  public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }
我们看看初始化操作都做了什么?

Cache cache =new DiskBasedCache(cacheDir)这个是网络数据的磁盘缓存

Network network = new BasicNetwork(stack);就是Network类

NetworkDispatcher[] mDispatchers=new NetworkDispatcher[threadPoolSize];就是请求数据的网络线程

ResponseDelivery mDelivery=new ExecutorDelivery(new Handler(Looper.getMainLooper())volley中默认的响应传递类

5、看下queue.start();这个方法,也就是最后一步启动线程进行数据访问,我们在RequestQueue看看start做了什么呢?

Volley最主要的功能其实就是跟网络打交道,然后从网络中获取相对应的数据,如果只有网络请求线程NetworkDispatcher,没有缓存线程(CacheDispatcher),显然不是很理想,所以在queue.start();方法中可以看到

    public void start() {
        stop();  
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

 mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();
生成了缓存线程CacheDispatcher,缓存中没有对应的记录的话,还是会将其扔到网络队列中,由网络线程(NetworkDispatcher)来干活

到此位置,我们就知道了构造方法中有磁盘缓存DiskBasedCache、Network类、网络主请求线程mDispatchers、请求结果的相应类ResponseDelivery、以及queue.start()中的网络缓存线程CacheDispatcher

我们之前写Volley的例子都是这样操作的:mQueue.add(stringRequest); 之类的操作

    public  Request add(Request request) {
        // Tag the request as belonging to this queue and add it to the set of current requests.
        request.setRequestQueue(this);
        synchronized (mCurrentRequests) {
            mCurrentRequests.add(request);
        }

        // Process requests in the order they are added.
        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        // If the request is uncacheable, skip the cache queue and go straight to the network.
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }

        // Insert request into stage if there&#39;s already a request with the same cache key in flight.
        synchronized (mWaitingRequests) {
            String cacheKey = request.getCacheKey();
            if (mWaitingRequests.containsKey(cacheKey)) {
                // There is already a request in flight. Queue up.
                Queue> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList>();
                }
                stagedRequests.add(request);
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                }
            } else {
                // Insert &#39;null&#39; queue for this cacheKey, indicating there is now a request in
                // flight.
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }
            return request;
        }
    }

可以看到,在!request.shouldCache()来判断要不要去缓存中查询,如果是去缓存中查询,那么就会把请求放到CacheQueue中,如果没有设置缓存则在mNetworkQueue.add(request);直接将这条请求加入网络请求队列。在默认情况下,每条请求都是可以缓存的,当然我们也可以调用Request的setShouldCache(false)方法来改变这一默认行为。

那么既然默认每条请求都是可以缓存的,自然就被添加到了缓存队列中,于是一直在后台等待的缓存线程就要开始运行起来了,

会去调用queue.start();那么就看看start方法中都干什么吧,如下:

    public void start() {
        stop();  // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it.
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

而默认情况下for循环会执行四次,也就是说当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行,不断等待网络请求的到来,其中1个CacheDispatcher是缓存线程,4个NetworkDispatcher是网络请求线程。

既然有5个线程运行,我们就先看看CacheDispatcher缓存线程做了什么操作?

	public void run() {
		if (DEBUG)
			VolleyLog.v("start new dispatcher");
		Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

		// 初始化缓存
		mCache.initialize();

		while (true) {
			try {
				// 从缓存队列中获取一个请求
				final Request request = mCacheQueue.take();
				request.addMarker("cache-queue-take");

				// 如果请求已经被取消,则重新获取请求
				if (request.isCanceled()) {
					request.finish("cache-discard-canceled");
					continue;
				}

				// 根据request的cacheKey从缓存中得到对应的记录
				Cache.Entry entry = mCache.get(request.getCacheKey());
				if (entry == null) {
					request.addMarker("cache-miss");
					// 这里说明缓存中没有对应的记录,那么需要去网络中获取,那么就将它放到Network的队列中
					mNetworkQueue.put(request);
					continue;
				}

				// 如果缓存中有记录,但是已经过期了或者失效了,也需要去网络获取,放到Network队列中
				if (entry.isExpired()) {
					request.addMarker("cache-hit-expired");
					request.setCacheEntry(entry);
					mNetworkQueue.put(request);
					continue;
				}

				// 如果上面的情况都不存在,说明缓存中存在这样记录,那么就调用request的parseNetworkResponse方法,获取一个响应Response
				request.addMarker("cache-hit");
				Response response = request
						.parseNetworkResponse(new NetworkResponse(entry.data,
								entry.responseHeaders));
				request.addMarker("cache-hit-parsed");

				if (!entry.refreshNeeded()) {
					// 缓存记录,不需要更新,那么就直接调用mDelivery,传回给主线程去更新。
					mDelivery.postResponse(request, response);
				} else {
					// 还存在这样一种情况,缓存记录存在,但是它约定的生存时间已经到了(还未完全过期,叫软过期),可以将其发送到主线程去更新
					// 但同时,也要从网络中更新它的数据
					request.addMarker("cache-hit-refresh-needed");
					request.setCacheEntry(entry);

					// Mark the response as intermediate.
					response.intermediate = true;

					// 将其传回主线程的同时,将请求放到Network队列中。
					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;
			}
		}
	}

缓存线程(CacheDispatcher)主要做了几件事情:

1)初始化本地缓存

2)开始一个无限的循环,调用 mCacheQueue的take方法,来获得一个请求,而mCacheQueue是一个BlockingQueue,也就是说,当队列中没有请求的时候,take方法就会一直阻塞在这里,等待队列中的请求,而一旦队列中有新的请求进来了,那么它就会马上执行下去。

3)判断请求是否已经取消,如果已经被取消了,则不需要再走下去。

4)根据请求的CacheKey去缓存中寻找相对应的记录,如果找不到对应的记录,或者对应的记录过期了,则将其放到NetworkQueue队列中。

5)缓存中存在相对应的记录,那么调用每个请求具体的实现方法 parseNetworkResponse函数,根据具体的请求去解析得到对应的响应Response对象。

6)获得Response对象之后,还会再进行判断这个请求是不是进行一次网络的更新,这是根据记录的soft-ttl (time-to-live)属性

从这里也可以看到,expired的判断跟refreshNeed的判断是两个字段,一个是ttl,一个是softTtl。

如果需要进行更新,那么就会在发送响应结果回主线程更新的同时,再将请求放到NetworkQueue中,从网络中更新请求对应的数据。如果不需要,则直接将结果调用mDelivery传回主线程进行UI的更新。

Volley最主要的功能其实就是跟网络打交道,然后从网络中获取相对应的数据,虽然有缓存线程(CacheDispatcher),但是如果缓存中没有对应的记录的话,还是会将其扔到网络队列中,由网络线程(NetworkDispatcher)来干活。

networkDispatcher.start();我们看看网络线程这里面做了什么操作?

    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request request;
        while (true) {
            try {
            	 // 从队列中获取一个请求,如果没有请求,则会一直阻塞  
                request = mQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker("network-queue-take");

             // 判断请求有没有取消,如果取消,则不必再继续  
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

             // 调用mNetwork去跟网络打交道 
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

             // 如果服务器返回一个未修改(304)的响应,并且这个请求已经发送过响应对象,不需要再继续,因为没改过  
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // 分析响应的数据,返回Response对象  
                Response response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                // 根据request的shouldCache字段来判断是不是需要缓存,如果需要,则将其放到mCache中。
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

             // 调用 mDelivery将Response对象传回主线程进行UI的更新。
                request.markDelivered();
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
            	//有错误,也会调用到mDelivery,将错误信息传回到主线程,进行提示  
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                mDelivery.postError(request, new VolleyError(e));
            }
        }
    }

网络线程(NetworkDispatcher)主要做了几件事情:

1)调用 mQueue的take()方法从队列中获取请求,如果没有请求,则一直阻塞在那里等待,直到队列中有新的请求到来。

2)判断请求有没有被取消,如果被取消,则重新获取请求。

3)调用Network对象将请求发送到网络中,并返回一个 NetworkResponse对象。

4)调用请求的pareseNetworkResonse方法,将NetworkResponse对象解析成相对应的Response对象。

5)判断请求是否需要缓存,如果需要缓存,则将其Response中cacheEntry对象放到缓存mCache中。

6)调用 mDelivery将Response对象传到主线程中进行UI更新。

可以看到,网络线程其实是调用 Network对象去实现跟网络进行沟通的,而在Volley中,默认的Network实现类,则是BasicNetwork类。我们去看下mNetwork.performRequest(request);做了什么操作?

  public NetworkResponse performRequest(Request request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map responseHeaders = new HashMap();
            try {
            	// 添加头部信息  
                Map headers = new HashMap();
                addCacheHeaders(headers, request.getCacheEntry());
              //调用HttpStack对象去网络中获取数据,返回一个HttpResponse对象
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();
//               获取服务器的响应头 数组,然后转为Map集合
                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
             // 从响应的状态行获取状态编码,如果是304(未修改),说明之前已经取过数据了,那么就直接利用缓存中的数据,构造一个NetworkResonse对象  
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
                            request.getCacheEntry() == null ? null : request.getCacheEntry().data,
                            responseHeaders, true);
                }

                // 有些响应是不带内容的,比如响应状态编码是204的话,添加一个空的byte作为内容,后面好统一处理。  
                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();
                }
                //构建NetworkResponse对象
                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);
                }
            }
        }
    }

BasicNetwork做的事情如下:

1)对于已经有缓存的请求,添加其头部信息

2)调用 HttpStack 对象去网络中获取数据,返回httpResonse 对象。

3)根据状态编码来返回不同的Response对象,如304(未修改)就返回缓存中的数据,如果不是,则根据响应中的数据,重新构造一个NetworkResponse对象。

4)BasicNetwork实现了重试的机制,如果第一次从网络获取失败,默认会重新再尝试一次,如果失败,则会将Error返回,默认的实现类是DefaultRetryPolicy类。

在上面的代码中httpResponse = mHttpStack.performRequest(request, headers);是通过HttpStack对象去请求返回HttpResponse对象,然后在获取HttpResponse对象的一些信息,然后封装为NetworkResponse返回给NetworkDispatcher,

我们要看下mHttpStack如何处理网络请求的?

 public HttpResponse performRequest(Request request, Map additionalHeaders)
            throws IOException, AuthFailureError {
        String url = request.getUrl();
        HashMap map = new HashMap();
        map.putAll(request.getHeaders());//默认为null 
        map.putAll(additionalHeaders);//添加头部,主要是缓存相关的头部信息  
        if (mUrlRewriter != null) {
            String rewritten = mUrlRewriter.rewriteUrl(url);
            if (rewritten == null) {
                throw new IOException("URL blocked by rewriter: " + url);
            }
            url = rewritten;
        }
        URL parsedUrl = new URL(url);
        HttpURLConnection connection = openConnection(parsedUrl, request);//打开Connection
        for (String headerName : map.keySet()) {
        	//将Map的对象添加到Connection的属性中
            connection.addRequestProperty(headerName, map.get(headerName));
        }
        //设置connection方法,主要是设置Method属性和Content(for post/put)  
        setConnectionParametersForRequest(connection, request);
        //设置Http 协议
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
        int responseCode = connection.getResponseCode();
        if (responseCode == -1) {
            throw new IOException("Could not retrieve response code from HttpUrlConnection.");
        }
        StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                connection.getResponseCode(), connection.getResponseMessage());
        
        BasicHttpResponse response = new BasicHttpResponse(responseStatus);
        //获得Response的流,并将其解析成对应的HttpEntity对象,设置给Response.entity字段
        response.setEntity(entityFromConnection(connection));
        for (Entry> header : connection.getHeaderFields().entrySet()) {
            if (header.getKey() != null) {
                Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
                response.addHeader(h);
            }
        }
        return response;
    }


HttpURLConnection是Android3.0以后才提供的一个网络访问类,而HurlStack类,也正是H(ttp)URL的缩写,所以这个类,其实就是基于HttpUrlConnection的实现,其步骤如下:

1)从Request中获得url参数,根据url参数构造URL对象,而URL对象是java提供的获取网络资源的一个封装好的实用类。
2)从URL对象打开Connection,并设置connection的超时,缓存,让网络资源写入等属性。

3)调用方法setConnectionParametersForRequest 来设置 Method属性,如果是Post或者Put的话,还要设置Content内容。

4)设置Http 协议,这里基本上是1.1了。
5)获得Response的流,并将其解析成对应的HttpEntity对象,设置给Response.entity字段,返回给BasicNetwork。

在HurlStack类中(BasicHttpResponse extends AbstractHttpMessage implements HttpResponse)

BasicHttpResponse response = new BasicHttpResponse(responseStatus); response.setEntity(entityFromConnection(connection));

然后在封装为HttpResponse对象返回给BasicNetwork对象

在BasicNetwork中在封装为 return new NetworkResponse(statusCode, responseContents, responseHeaders, false);返回给NetworkDispatcher对象

最后在NetworkDispatcher中,进行 Responseresponse = request.parseNetworkResponse(networkResponse);将请求结果解析成需要的类型,将NetworkResponse解析成Response

下面的代码拿StringRequest方式说明

    protected Response 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));
    }

  public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
        long now = System.currentTimeMillis();

        Map headers = response.headers;

        long serverDate = 0;
        long serverExpires = 0;
        long softExpire = 0;
        long maxAge = 0;
        boolean hasCacheControl = false;

        String serverEtag = null;
        String headerValue;

        headerValue = headers.get("Date");
        if (headerValue != null) {
            serverDate = parseDateAsEpoch(headerValue);
        }

        headerValue = headers.get("Cache-Control");
        if (headerValue != null) {
            hasCacheControl = true;
            String[] tokens = headerValue.split(",");
            for (int i = 0; i < tokens.length; i++) {
                String token = tokens[i].trim();
                if (token.equals("no-cache") || token.equals("no-store")) {
                    return null;
                } else if (token.startsWith("max-age=")) {
                    try {
                        maxAge = Long.parseLong(token.substring(8));
                    } catch (Exception e) {
                    }
                } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
                    maxAge = 0;
                }
            }
        }

        headerValue = headers.get("Expires");
        if (headerValue != null) {
            serverExpires = parseDateAsEpoch(headerValue);
        }

        serverEtag = headers.get("ETag");

        // Cache-Control takes precedence over an Expires header, even if both exist and Expires
        // is more restrictive.
        if (hasCacheControl) {
            softExpire = now + maxAge * 1000;
        } else if (serverDate > 0 && serverExpires >= serverDate) {
            // Default semantic for Expire header in HTTP specification is softExpire.
            softExpire = now + (serverExpires - serverDate);
        }

        Cache.Entry entry = new Cache.Entry();
        entry.data = response.data;
        entry.etag = serverEtag;
        entry.softTtl = softExpire;
        entry.ttl = entry.softTtl;
        entry.serverDate = serverDate;
        entry.responseHeaders = headers;

        return entry;
    }
然后NetworkDispatcher拿着解析好的 Responseresponse东东, 去mDelivery.postResponse(request, response);进行ui更新操作

回头看NetworkDispatcher类run方法中的 mDelivery.postResponse(request, response);如何去更新ui界面的?

请求结果的交付是通过ResponseDelivery接口完成的,它有一个实现类ExecutorDelivery, 主要有postResponse()与postError()两个方法,分别在请求成功或失败时将结果提交给请求发起者。

看 mDelivery.postResponse(request, response);方法的具体实现。每post一个response,都会调用ResponseDeliveryRunnable的run()方法。在这个run()方法中,会通过mRequest.deliverResponse(mResponse.result)来传递response的result,这个result其实就是已经解析好的响应结果,比如一个表示处理结果的字符串或一个User对象

 @Override
    public void postResponse(Request request, Response response) {
        postResponse(request, response, null);
    }

    @Override
    public void postResponse(Request request, Response response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }

    @Override
    public void postError(Request request, VolleyError error) {
        request.addMarker("post-error");
        Response response = Response.error(error);
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
    }

如下是run方法
        public void run() {
            // If this request has canceled, finish it and don&#39;t deliver.
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            // Deliver a normal response or error, depending.
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

            // If this is an intermediate response, add a marker, otherwise we&#39;re done
            // and the request can be finished.
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                mRequest.finish("done");
            }

            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) {
                mRunnable.run();
            }
       }

在mRequest.deliverResponse(mResponse.result)方法中,有ImageRequest、JsonRequest、StringRequest请求方式

\

我们就简单看StringRequest的实现方法,这里有一个接口去外部实现

  protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }
是通过构造方式传入进来接口、默认是get方式

   public StringRequest(int method, String url, Listener listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }
    public StringRequest(String url, Listener listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }


这里还有一个问题, 因为更新UI的操作只能在主线程中进行,那么ResponseDeliveryRunnable的run()方法不能再新起一个线程来执行,而应该在主线程中执行,这个是如何做到的?

其实还是用的Handler,Looper,MessageQueue的那套机制。 在Volley初始化一个RequestQueue的时候,会调用RequestQueue的如下构造函数,它构建了一个ExecutorDelivery对象,并把一个与主线程的Looper关联的一个Handler,大家还记得如下的构造方法没?

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

然后再看下ExecutorDelivery的构造方法, 通过handler的post方法,把ResponseDeliveryRunnable 这个runnable加到了主线程的消息队列中,所以它的run()方法是在主线程中执行的。

   public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }


相关文章
最新文章
热点推荐