这是 volley 的第四篇 blog 了,写完这篇,volley 的大部分用法也都算写了一遍,所以暂时不会写 volley 的文章了,如果想看我前面写的文章,可以点这里 Android volley 解析(三)之文件上传篇
为什么要用缓存
我们知道,当客户端在请求网络数据的时候,是需要消耗流量的,特别是对于移动端用户来说,对于流量的控制要求很高。所以在做网络请求的时候,如果对数据更新要求不是特别高,往往都会用到缓存机制,一方面能减少对服务端的请求,控制流量;另一方面,当客户端在没有网络的情况下也能看到上一次请求的数据,这样使用户体验更佳,如下图,微信朋友圈在没有网络的情况下,依然能看到朋友们的动态
volley 缓存的原理
看了我前面blog 的朋友一定还记得,我在讲 get 请求的时候,讲到了volley 的基本流程,首先启动的是缓存线程mCacheDispatcher来获取缓存;
那我们就从如何获取缓存开始分析;
1、初始化缓存
<code class="hljs r has-numbering"> @Override public void run() { <span class="hljs-keyword">if</span> (DEBUG) VolleyLog.v(<span class="hljs-string">"start new dispatcher"</span>); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // Make a blocking call to initialize the cache.这里对缓存做初始化操作 mCache.initialize(); <span class="hljs-keyword">while</span> (true) { <span class="hljs-keyword">...</span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>
2、获取缓存
<code class="hljs java has-numbering"> <span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) { <span class="hljs-keyword">try</span> { <span class="hljs-comment">// Get a request from the cache triage queue, blocking until</span> <span class="hljs-comment">// at least one is available.从缓存队列中获取缓存</span> <span class="hljs-keyword">final</span> Request request = mCacheQueue.take(); request.addMarker(<span class="hljs-string">"cache-queue-take"</span>); <span class="hljs-comment">// If the request has been canceled, don't bother dispatching it.</span> ...</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul>
这里有一个问题,缓存队列在什么时候添加缓存请求呢?我们回到最开始请求队列添加请求的地方
<code class="hljs java has-numbering"> <span class="hljs-keyword">public</span> Request <span class="hljs-title">add</span>(Request request) { <span class="hljs-comment">// Tag the request as belonging to this queue and add it to the set of current requests.</span> request.setRequestQueue(<span class="hljs-keyword">this</span>); <span class="hljs-keyword">synchronized</span> (mCurrentRequests) { mCurrentRequests.add(request); } <span class="hljs-comment">// Process requests in the order they are added.</span> request.setSequence(getSequenceNumber()); request.addMarker(<span class="hljs-string">"add-to-queue"</span>); <span class="hljs-comment">// If the request is uncacheable, skip the cache queue and go straight to the network.如果请求没有设置缓存,则把请求添加到网络队列中</span> <span class="hljs-keyword">if</span> (!request.shouldCache()) { mNetworkQueue.add(request); <span class="hljs-keyword">return</span> request; } <span class="hljs-comment">// Insert request into stage if there's already a request with the same cache key in flight.</span> <span class="hljs-keyword">synchronized</span> (mWaitingRequests) { String cacheKey = request.getCacheKey(); <span class="hljs-keyword">if</span> (mWaitingRequests.containsKey(cacheKey)) { <span class="hljs-comment">// There is already a request in flight. Queue up.</span> Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey); <span class="hljs-keyword">if</span> (stagedRequests == <span class="hljs-keyword">null</span>) { stagedRequests = <span class="hljs-keyword">new</span> LinkedList<Request>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); <span class="hljs-keyword">if</span> (VolleyLog.DEBUG) { VolleyLog.v(<span class="hljs-string">"Request for cacheKey=%s is in flight, putting on hold."</span>, cacheKey); } } <span class="hljs-keyword">else</span> { <span class="hljs-comment">// Insert 'null' queue for this cacheKey, indicating there is now a request in</span> <span class="hljs-comment">// flight.</span> mWaitingRequests.put(cacheKey, <span class="hljs-keyword">null</span>); <span class="hljs-comment">//这里添加请求到缓存队列中</span> mCacheQueue.add(request); } <span class="hljs-keyword">return</span> request; } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li></ul>
代码不长,也很好理解,如果我们的请求没有设置了缓存,则请求添加到网络请求队列中,并直接返回了,不往下执行了,这时缓存队列中无法获取请求,所以这里我们知道了,想要用缓存则需要
在 Request 中把
<code class="hljs java has-numbering"> <span class="hljs-comment">//设置是否启用缓存</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setShouldCache</span>(<span class="hljs-keyword">boolean</span> shouldCache) { mShouldCache = shouldCache; }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul>
设为 true,当然,我们看mShouldCache 的默认值
<code class="hljs java has-numbering"> <span class="hljs-javadoc">/** * Whether or not responses to this request should be cached. */</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> mShouldCache = <span class="hljs-keyword">true</span>;</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul>
volley默认启用缓存的。再往下看
<code class="hljs cs has-numbering"> <span class="hljs-comment">// If the request has been canceled, don't bother dispatching it.如果已经取消请求,则结束本次请求的所有操作</span> <span class="hljs-keyword">if</span> (request.isCanceled()) { request.finish(<span class="hljs-string">"cache-discard-canceled"</span>); <span class="hljs-keyword">continue</span>; } <span class="hljs-comment">// Attempt to retrieve this item from cache. 通过缓存类获取缓存</span> Cache.Entry entry = mCache.<span class="hljs-keyword">get</span>(request.getCacheKey()); <span class="hljs-comment">//如果获取的缓存为空,这里有两种情况为空,一,第一次换取,没有情求过网络;二,缓存的数据达到了最大限度,此缓存已经被删除。</span> <span class="hljs-keyword">if</span> (entry == <span class="hljs-keyword">null</span>) { request.addMarker(<span class="hljs-string">"cache-miss"</span>); Log.i(<span class="hljs-string">"CacheDispatcher"</span>, <span class="hljs-string">"没有缓存数据:"</span> + request.getUrl()); mNetworkQueue.put(request); <span class="hljs-keyword">continue</span>; } <span class="hljs-comment">// If it is completely expired, just send it to the network.缓存已经过期,则重新请求网络</span> <span class="hljs-keyword">if</span> (entry.isExpired()) { request.addMarker(<span class="hljs-string">"cache-hit-expired"</span>); request.setCacheEntry(entry); Log.i(<span class="hljs-string">"CacheDispatcher"</span>, <span class="hljs-string">"缓存数据过期:"</span> + request.getUrl()); mNetworkQueue.put(request); <span class="hljs-keyword">continue</span>; } <span class="hljs-comment">// We have a cache hit; parse its data for delivery back to the request.</span> request.addMarker(<span class="hljs-string">"cache-hit"</span>); <span class="hljs-comment">//到这里表示已经成功获取缓存数据</span> Response<?> response = request.parseNetworkResponse( <span class="hljs-keyword">new</span> NetworkResponse(entry.data, entry.responseHeaders)); request.addMarker(<span class="hljs-string">"cache-hit-parsed"</span>); <span class="hljs-comment">//如果缓存需要刷新,则请求网络</span> <span class="hljs-keyword">if</span> (!entry.refreshNeeded()) { <span class="hljs-comment">// Completely unexpired cache hit. Just deliver the response.</span> Log.i(<span class="hljs-string">"CacheDispatcher"</span>, <span class="hljs-string">"获取缓存数据:"</span> + request.getUrl()); mDelivery.postResponse(request, response); } <span class="hljs-keyword">else</span> { <span class="hljs-comment">// Soft-expired cache hit. We can deliver the cached response,</span> <span class="hljs-comment">// but we need to also send the request to the network for</span> <span class="hljs-comment">// refreshing.</span> request.addMarker(<span class="hljs-string">"cache-hit-refresh-needed"</span>); request.setCacheEntry(entry); <span class="hljs-comment">// Mark the response as intermediate.</span> response.intermediate = <span class="hljs-keyword">true</span>; <span class="hljs-comment">// Post the intermediate response back to the user and have</span> <span class="hljs-comment">// the delivery then forward the request along to the network.</span> mDelivery.postResponse(request, response, <span class="hljs-keyword">new</span> Runnable() { @Override <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() { <span class="hljs-keyword">try</span> { mNetworkQueue.put(request); } <span class="hljs-keyword">catch</span> (InterruptedException e) { <span class="hljs-comment">// Not much we can do about this.</span> } } }); } } <span class="hljs-keyword">catch</span> (InterruptedException e) { <span class="hljs-comment">// We may have been interrupted because it was time to quit.</span> <span class="hljs-keyword">if</span> (mQuit) { <span class="hljs-keyword">return</span>; } <span class="hljs-keyword">continue</span>; } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li></ul>
直接从缓存类Cache获取,并经过几次验证,如果缓存合法则解析然后交给 UI线程分发出去。下面来看看具体的流程
存储缓存
如果是第一次请求,或者缓存已过期,肯定是无法获取到缓存的,这时可根据上图分析,将会进入网络请求线程NetworkDispatcher,储存缓存毫无疑问也是在这里面实现的。
<code class="hljs avrasm has-numbering"> //省略部分代码 if (networkResponse<span class="hljs-preprocessor">.notModified</span> && request<span class="hljs-preprocessor">.hasHadResponseDelivered</span>()) { request<span class="hljs-preprocessor">.finish</span>(<span class="hljs-string">"not-modified"</span>)<span class="hljs-comment">;</span> continue<span class="hljs-comment">;</span> } // Parse the response here on the worker thread. Response<?> response = request<span class="hljs-preprocessor">.parseNetworkResponse</span>(networkResponse)<span class="hljs-comment">;</span> request<span class="hljs-preprocessor">.addMarker</span>(<span class="hljs-string">"network-parse-complete"</span>)<span class="hljs-comment">;</span> // Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for <span class="hljs-number">304</span>s.如果需要缓存,并且用户已经把网络请求的数据转换成一份为缓存数据,则通过 Cache 类把缓存存储。 if (request<span class="hljs-preprocessor">.shouldCache</span>() && response<span class="hljs-preprocessor">.cacheEntry</span> != null) { mCache<span class="hljs-preprocessor">.put</span>(request<span class="hljs-preprocessor">.getCacheKey</span>(), response<span class="hljs-preprocessor">.cacheEntry</span>)<span class="hljs-comment">;</span> request<span class="hljs-preprocessor">.addMarker</span>(<span class="hljs-string">"network-cache-written"</span>)<span class="hljs-comment">;</span> } // Post the response back. request<span class="hljs-preprocessor">.markDelivered</span>()<span class="hljs-comment">;</span> mDelivery<span class="hljs-preprocessor">.postResponse</span>(request, response)<span class="hljs-comment">;</span> } catch (VolleyError volleyError) { parseAndDeliverNetworkError(request, volleyError)<span class="hljs-comment">;</span> } catch (Exception e) { VolleyLog<span class="hljs-preprocessor">.e</span>(e, <span class="hljs-string">"Unhandled exception %s"</span>, e<span class="hljs-preprocessor">.toString</span>())<span class="hljs-comment">;</span> mDelivery<span class="hljs-preprocessor">.postError</span>(request, new VolleyError(e))<span class="hljs-comment">;</span> } //省略部分代码</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li></ul>
通过以上代码可以知道,在网络请求线程请求到数据以后,会交给用户解析,并把数据转换一份成缓存数据,通过 Cache 缓存操作类,把数据缓存起来。
网络数据转换成缓存数据
上面提到了,把网络数据转化成缓存数据,那么,volley 是如何转换的?
<code class="hljs cs has-numbering"> */ <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Cache.Entry <span class="hljs-title">parseCacheHeaders</span>(NetworkResponse response) { <span class="hljs-keyword">long</span> now = System.currentTimeMillis(); <span class="hljs-comment">//获取网络请求数据的头信息</span> Map<String, String> headers = response.headers; <span class="hljs-keyword">long</span> serverDate = <span class="hljs-number">0</span>; <span class="hljs-keyword">long</span> serverExpires = <span class="hljs-number">0</span>; <span class="hljs-keyword">long</span> softExpire = <span class="hljs-number">0</span>; <span class="hljs-keyword">long</span> maxAge = <span class="hljs-number">0</span>; boolean hasCacheControl = <span class="hljs-keyword">false</span>; String serverEtag = <span class="hljs-keyword">null</span>; String headerValue; <span class="hljs-comment">//从头信息中获取 Date 数据</span> headerValue = headers.<span class="hljs-keyword">get</span>(<span class="hljs-string">"Date"</span>); <span class="hljs-keyword">if</span> (headerValue != <span class="hljs-keyword">null</span>) { serverDate = parseDateAsEpoch(headerValue); } <span class="hljs-comment">//从头信息中获取 Cache-Control 数据,来控制缓存</span> headerValue = headers.<span class="hljs-keyword">get</span>(<span class="hljs-string">"Cache-Control"</span>); <span class="hljs-keyword">if</span> (headerValue != <span class="hljs-keyword">null</span>) { hasCacheControl = <span class="hljs-keyword">true</span>; String[] tokens = headerValue.split(<span class="hljs-string">","</span>); <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < tokens.length; i++) { String token = tokens[i].trim(); <span class="hljs-keyword">if</span> (token.equals(<span class="hljs-string">"no-cache"</span>) || token.equals(<span class="hljs-string">"no-store"</span>)) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>; } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (token.startsWith(<span class="hljs-string">"max-age="</span>)) { <span class="hljs-keyword">try</span> { maxAge = Long.parseLong(token.substring(<span class="hljs-number">8</span>)); } <span class="hljs-keyword">catch</span> (Exception e) { } } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (token.equals(<span class="hljs-string">"must-revalidate"</span>) || token.equals(<span class="hljs-string">"proxy-revalidate"</span>)) { maxAge = <span class="hljs-number">0</span>; } } } headerValue = headers.<span class="hljs-keyword">get</span>(<span class="hljs-string">"Expires"</span>); <span class="hljs-keyword">if</span> (headerValue != <span class="hljs-keyword">null</span>) { serverExpires = parseDateAsEpoch(headerValue); } serverEtag = headers.<span class="hljs-keyword">get</span>(<span class="hljs-string">"ETag"</span>); <span class="hljs-comment">// Cache-Control takes precedence over an Expires header, even if both exist and Expires</span> <span class="hljs-comment">// is more restrictive.</span> <span class="hljs-keyword">if</span> (hasCacheControl) { softExpire = now + maxAge * <span class="hljs-number">1000</span>; } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (serverDate > <span class="hljs-number">0</span> && serverExpires >= serverDate) { <span class="hljs-comment">// Default semantic for Expire header in HTTP specification is softExpire.</span> softExpire = now + (serverExpires - serverDate); } Cache.Entry entry = <span class="hljs-keyword">new</span> Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; entry.ttl = entry.softTtl; entry.serverDate = serverDate; entry.responseHeaders = headers; <span class="hljs-keyword">return</span> entry; }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li></ul>
前面我们提到的缓存是否过期和是否需要刷新,都是通过 response 的头部信息来判断,但是在BasicNetwork中只实现了缓存是否过期的操作,没有实现缓存是否需要刷新
<code class="hljs java has-numbering"> <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> NetworkResponse <span class="hljs-title">performRequest</span>(Request<?> request) <span class="hljs-keyword">throws</span> VolleyError { <span class="hljs-keyword">long</span> requestStart = SystemClock.elapsedRealtime(); <span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) { HttpResponse httpResponse = <span class="hljs-keyword">null</span>; <span class="hljs-keyword">byte</span>[] responseContents = <span class="hljs-keyword">null</span>; Map<String, String> responseHeaders = <span class="hljs-keyword">new</span> HashMap<String, String>(); <span class="hljs-keyword">try</span> { <span class="hljs-comment">// Gather headers.</span> Map<String, String> headers = <span class="hljs-keyword">new</span> HashMap<String, String>(); addCacheHeaders(headers, request.getCacheEntry()); httpResponse = mHttpStack.performRequest(request, headers); StatusLine statusLine = httpResponse.getStatusLine(); <span class="hljs-keyword">int</span> statusCode = statusLine.getStatusCode(); responseHeaders = convertHeaders(httpResponse.getAllHeaders()); <span class="hljs-comment">//从这里开始设置缓存信息,如果设置了缓存的缓存时间,则把它添加到头部信息中,但是没有实现是否需要刷新缓存的操作,如果有需要,也可以在这里实现,这是就需要修改源码。</span> <span class="hljs-keyword">if</span>(request.getCacheTime() != <span class="hljs-number">0</span>){ responseHeaders.put(<span class="hljs-string">"Cache-Control"</span>,<span class="hljs-string">"max-age="</span> + request.getCacheTime()); } <span class="hljs-comment">// Handle cache validation.</span> <span class="hljs-keyword">if</span> (statusCode == HttpStatus.SC_NOT_MODIFIED) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> NetworkResponse(HttpStatus.SC_NOT_MODIFIED, request.getCacheEntry().data, responseHeaders, <span class="hljs-keyword">true</span>); } responseContents = entityToBytes(httpResponse.getEntity()); <span class="hljs-comment">// if the request is slow, log it.</span> <span class="hljs-keyword">long</span> requestLifetime = SystemClock.elapsedRealtime() - requestStart; logSlowRequests(requestLifetime, request, responseContents, statusLine); <span class="hljs-keyword">if</span> (statusCode != HttpStatus.SC_OK && statusCode != HttpStatus.SC_NO_CONTENT) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IOException(); } <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> NetworkResponse(statusCode, responseContents, responseHeaders, <span class="hljs-keyword">false</span>); } <span class="hljs-keyword">catch</span> (SocketTimeoutException e) { attemptRetryOnException(<span class="hljs-string">"socket"</span>, request, <span class="hljs-keyword">new</span> TimeoutError()); } <span class="hljs-keyword">catch</span> (ConnectTimeoutException e) { attemptRetryOnException(<span class="hljs-string">"connection"</span>, request, <span class="hljs-keyword">new</span> TimeoutError()); } <span class="hljs-keyword">catch</span> (MalformedURLException e) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"Bad URL "</span> + request.getUrl(), e); } <span class="hljs-keyword">catch</span> (IOException e) { <span class="hljs-keyword">int</span> statusCode = <span class="hljs-number">0</span>; NetworkResponse networkResponse = <span class="hljs-keyword">null</span>; <span class="hljs-keyword">if</span> (httpResponse != <span class="hljs-keyword">null</span>) { statusCode = httpResponse.getStatusLine().getStatusCode(); } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NoConnectionError(e); } VolleyLog.e(<span class="hljs-string">"Unexpected response code %d for %s"</span>, statusCode, request.getUrl()); <span class="hljs-keyword">if</span> (responseContents != <span class="hljs-keyword">null</span>) { networkResponse = <span class="hljs-keyword">new</span> NetworkResponse(statusCode, responseContents, responseHeaders, <span class="hljs-keyword">false</span>); <span class="hljs-keyword">if</span> (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) { attemptRetryOnException(<span class="hljs-string">"auth"</span>, request, <span class="hljs-keyword">new</span> AuthFailureError(networkResponse)); } <span class="hljs-keyword">else</span> { <span class="hljs-comment">// TODO: Only throw ServerError for 5xx status codes.</span> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ServerError(networkResponse); } } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NetworkError(networkResponse); } } } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li></ul>
如何使用缓存
根据上面分析不难发现,要使用缓存,得具备两个条件,
1、启用缓存
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setShouldCache</span>(<span class="hljs-keyword">boolean</span> shouldCache) { mShouldCache = shouldCache; }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>
不过这个条件默认情况下是开启的。
2、设置缓存的时间
<code class="hljs cs has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setCacheTime</span>(<span class="hljs-keyword">long</span> cacheTime) { mCacheTime = cacheTime; }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>
这里 cacheTime 的单位是秒。
接下来看看具体用法
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CacheRequestActivity</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ActionBarActivity</span> {</span> <span class="hljs-comment">/*数据显示的View*/</span> <span class="hljs-keyword">private</span> TextView mIdTxt,mNameTxt,mDownloadTxt,mLogoTxt,mVersionTxt ; <span class="hljs-comment">/*弹出等待对话框*/</span> <span class="hljs-keyword">private</span> ProgressDialog mDialog ; <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) { <span class="hljs-keyword">super</span>.onCreate(savedInstanceState); setContentView(R.layout.activity_get); mIdTxt = (TextView) findViewById(R.id.id_id) ; mNameTxt = (TextView) findViewById(R.id.id_name) ; mDownloadTxt = (TextView) findViewById(R.id.id_download) ; mLogoTxt = (TextView) findViewById(R.id.id_logo) ; mVersionTxt = (TextView) findViewById(R.id.id_version) ; mDialog = <span class="hljs-keyword">new</span> ProgressDialog(<span class="hljs-keyword">this</span>) ; mDialog.setMessage(<span class="hljs-string">"get请求中..."</span>); mDialog.show(); <span class="hljs-comment">/*请求网络获取数据*/</span> MiNongApi.CacheObjectMiNongApi(<span class="hljs-string">"minongbang"</span>, <span class="hljs-keyword">new</span> ResponseListener<TestBean>() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onErrorResponse</span>(VolleyError error) { mDialog.dismiss(); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onResponse</span>(TestBean response) { Log.v(<span class="hljs-string">"zgy"</span>,<span class="hljs-string">"=======response======="</span>+response) ; mDialog.dismiss(); <span class="hljs-comment">/*显示数据*/</span> mIdTxt.setText(response.getId() + <span class="hljs-string">""</span>); mNameTxt.setText(response.getName()); mDownloadTxt.setText(response.getDownload() + <span class="hljs-string">""</span>); mLogoTxt.setText(response.getLogo()); mVersionTxt.setText(response.getVersion() + <span class="hljs-string">""</span>); } }); } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li></ul>
cache api
<code class="hljs cs has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">CacheObjectMiNongApi</span>(String <span class="hljs-keyword">value</span>,ResponseListener listener){ String url ; <span class="hljs-keyword">try</span> { url = Constant.MinongHost +<span class="hljs-string">"?test="</span>+ URLEncoder.encode(<span class="hljs-keyword">value</span>, <span class="hljs-string">"utf-8"</span>) ; } <span class="hljs-keyword">catch</span> (UnsupportedEncodingException e) { e.printStackTrace(); url = Constant.MinongHost +<span class="hljs-string">"?test="</span>+ URLEncoder.encode(<span class="hljs-keyword">value</span>) ; } Request request = <span class="hljs-keyword">new</span> GetObjectRequest(url,<span class="hljs-keyword">new</span> TypeToken<TestBean>(){}.getType(),listener) ; <span class="hljs-comment">//请用缓存</span> request.setShouldCache(<span class="hljs-keyword">true</span>); <span class="hljs-comment">//设置缓存时间10分钟</span> request.setCacheTime(<span class="hljs-number">10</span>*<span class="hljs-number">60</span>); VolleyUtil.getRequestQueue().add(request) ; }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li></ul>
再来看看效果图,在缓存存在的情况下当把网络连接断开的时候,依然能够获取到数据
有一种情况需要注意:当需要获取缓存,且希望缓存刷新的时候,这种情况就需要修改 Volley 的源码,前面已经提到一点点,相信大家都能实现的。
转载自:http://blog.youkuaiyun.com/jxxfzgy/article/details/44088299