1、基本使用
Volley适合轻量、高并发的网络请求,但如果大数据量的操作,比如上传下载文件,就不太适合了。使用Volley主要有以下几个步奏:
1、设置、启动请求队列RequestQueue
- RequestQueue只有一个
- 需要的Context是Application context,不是Activity context
- 在调用newRequestQueue()方法的时候,内部已经调用了queue.start(),如果再调用这个方法,可能会有异常。
RequestQueue queue= Volley.newRequestQueue(context.getApplicationContext());
RequestQueue 管理若干个工作线程:缓存处理线程、网络请求处理线程(4)
2、新建请求对象 Request<T>
Request对象主要负责解析原始的响应数据(parseNetworkResponse())
Request<T> request=new Request<>(...);
volley默认提供了四种Request:StringRequest、JsonArrayRequest、JsonObjectRequest、ImageRequest。具体用法参考:Android Volley完全解析(一),初识Volley的基本用法、 Android Volley完全解析(二),使用Volley加载网络图片
3、将Request添加到请求队列中
queue.add(request);
4、取消请求
- 给request设置tag:request.setTag(tag)
- 然后可以在onstop()方法 取消对应的请求:queue.cancelAll(tag)、
queue.cancelAll(tag)、
5、在请求出错时,怎么使用缓存的数据?
/**
* 请求出错时,从缓存中取出数据
* @param error
*/
@Override
public void deliverError(VolleyError error) {
if (error instanceof NoConnectionError) {
Cache.Entry entry = this.getCacheEntry();
if(entry != null) {
Response<T> response = parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
deliverResponse(response.result);
return;
}
}
super.deliverError(error);
}
6、发送post请求
重写getParams()方法,因为在发送post数据的时候,会调用 request.getBody()--->request.getParams()方法
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return map;
}
7、超时重试
开发时,如果遇到一个request(尤其是post),被发送了多次。。。
可以使用DefaultRetryPolicy,重新设置这个请求的超时时间
// 设置超时时间。要确保最大重试次数为1,以保证超时后不重新请求
request.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
8、当然使用Volley,需要先引入Volley:
你可以将它下载到本地:
git clone https://android.googlesource.com/platform/frameworks/volley
或者使用gradle
compile 'com.android.volley:volley:1.0.0'
最后不要忘了权限
<uses-permission android:name="android.permission.INTERNET"/>
2、几个概念
1、状态码---204、304
服务器都没有返回响应的主体
当判定缓存过期后, 会向源服务器确认资源的有效性。
2、Expires
代表资源的失效日期
Cache-Control 的max-age 指令和 Expires都可以代表资源的失效日期,当它们同时存在时会优先处理 max-age 指令。
3、HttpURLConnection的使用
1、建立HttpURLConnection 对象
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
2、设置连接\请求属性
- 设置连接服务器超时、从服务器读取数据超时
connection.setConnectTimeout(timeoutMs);
connection.setReadTimeout(timeoutMs);
如果不设置超时(timeout),在网络异常的情况下,可能会导致程序不继续往下执行
- 不使用缓存
connection.setUseCaches(false);
- 允许从连接中读取数据,默认为true
connection.setDoInput(true);
- 允许向连接中写入数据,默认为false,post的时候要用
connection.setDoOutput(true);
- 设置请求的方式,post
connection.setRequestMethod("POST");
- 设置请求属性
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
如上,connection的配置必须要在connect之前完成
3、连接
connection.connect();
或者
connection.getOutputStream()
或者
connection.getInputStream()
HttpURLConnection的connect()函数,实际上只是建立了一个与服务器的tcp连接,并没有实际发送http请求。 无论是post还是get,http请求实际上直到HttpURLConnection的getInputStream()这个函数里面才正式发送出去。
4、发送post请求
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
在http头后面紧跟着的是http请求的正文,正文的内容是通过outputStream流写入的, 实际上outputStream不是一个网络流,充其量是个字符串流,往里面写入的东西不会立即发送到网络, 而是存在于内存缓冲区中,待outputStream流关闭时,根据输入的内容生成http正文。
至此,http请求的东西已经全部准备就绪。在getInputStream()函数调用的时候,就会把准备好的http请求 正式发送到服务器了,然后返回一个输入流,用于读取服务器对于此次http请求的返回信息。由于http 请求在getInputStream的时候已经发送出去了(包括http头和正文),因此在getInputStream()函数之后对connection对象进行设置(对http头的信息进行修改)或者写入outputStream(对正文进行修改)都是没有意义的了,执行这些操作会导致异常的发生。
5、获取响应
connection.getResponseCode();
connection.getResponseMessage();
connection.getInputStream();
connection.getHeaderFields();
3、分析

1、网络请求
//Volley是如何设置请求报文(报文首部、报文主体)?响应报文是如何被缓存的?
Volley的网络请求从NetworkDispatcher开始,经过mNetwork(BasicNetwork)的performRequest,最终由HttpStack(HurlStack)的performRequest执行具体的网络请求并处理响应。然后通过request.parseNetworkResponse(),解析响应的数据。最后调用mDelivery,将结果分发到主线程。在这个过程中会根据条件,决定是否解析和分发数据,是否缓存数据
1、NetworkDispatcher主要做了下面几件事:
- 从mQueue中取出一个请求,通过mNetwork执行网络请求
- 如果服务器返回的是304,同时response已经被缓存处理程序分发过了,不分发数据
- 通过request.parseNetworkResponse(),解析服务器返回的响应 ,
- 同时,如果满足条件,调用mCache缓存响应(请求的响应需要缓存,同时response.cacheEntry != null)
- 调用mDelivery,将结果分发到主线程
public class NetworkDispatcher extends Thread {
...
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
Request<?> request = mQueue.take();
// Perform the network request.
NetworkResponse networkResponse = mNetwork.performRequest(request);
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical 一样的 response.
//在新建networkResponse时,为notModified赋值
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
Response<?> response = request.parseNetworkResponse(networkResponse);
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
//在自定义的request里赋值的
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
}
// Post the response back.
request.markDelivered();
// 分发 响应和错误到主线程
mDelivery.postResponse(request, response);
}
}
}
2、mNetwork(BasicNetwork)的performRequest
- 通过HttpStack(HurlStack)的performRequest,执行具体的网络请求(2)
- 根据状态码(204/304/...),返回不同的NetworkResponse
public class BasicNetwork implements Network {
...
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation. 304
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// Some responses such as 204s do not have content. We must check.
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];
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
}
}
}
3、HttpStack(HurlStack)的performRequest
- 创建HttpURLConnection并设置连接/请求参数,
- 连接,
- 初始化HttpResponse对象
- 设置responseStatus
- 设置Entity
- 设置Header
public class HurlStack implements HttpStack {
...
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
{
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
URL parsedUrl = new URL(url);
//连接,设置连接参数
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
//设置请求属性
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
StatusLine responseStatus = new BasicStatusLine(protocolVersion,connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
private static HttpEntity entityFromConnection(HttpURLConnection connection) {
BasicHttpEntity entity = new BasicHttpEntity();
InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
entity.setContent(inputStream);
//大小
entity.setContentLength(connection.getContentLength());
//服务器对实体主体部分采用的编码方式
entity.setContentEncoding(connection.getContentEncoding());
//实体主体内对象的媒体类型
entity.setContentType(connection.getContentType());
return entity;
}
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);
// 允许从 URL connection 中读取
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;
}
static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
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.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
default:
throw new IllegalStateException("Unknown method type.");
}
}
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
throws IOException, AuthFailureError {
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
byte[] body = request.getBody();
if (body != null) {
addBody(connection, request, body);
}
}
// todo 添加post请求 报文主体部分 (参数部分)
private static void addBody(HttpURLConnection connection, Request<?> request, byte[] body)
throws IOException, AuthFailureError {
// 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.
// 允许向 URL connection 中写入
connection.setDoOutput(true);
//设定传送的内容类型是可序列化的java对象
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
//getOutputStream会隐含的进行connect,连接
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
//在调用下边的getInputStream()函数时才把准备好的http请求正式发送到服务器
}
}
2、缓存
- 从缓存队列里取出一个请求
- 根据这个请求获取它的缓存
- 如果缓存为空或者缓存过期,将请求放到网络请求队列里
- 解析缓存
- 调用mDelivery,将结果分发到主线程
public class CacheDispatcher extends Thread {
@Override
public void run() {
// Make a blocking call to initialize the cache.
mCache.initialize();
while (true) {
// Get a request from the cache triage queue, blocking until at least one is available.
final Request<?> request = mCacheQueue.take();
// Attempt to retrieve this item from cache.
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
}
// If it is completely expired, just send it to the network.
if (entry.isExpired()) {
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
mDelivery.postResponse(request, response);
} else {
// Soft-expired cache hit. We can deliver the cached response,
// but we need to also send the request to the network for
// refreshing.
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
mNetworkQueue.put(request);
}
});
}
}
}
}
3、这个 Cache.Entry是在什么地方初始化的呢?
在NetworkDispatcher中,如果允许缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
}
response是在request 的 parseNetworkResponse中赋值的
public class StringRequest extends Request<String> {
...
@Override
protected void deliverResponse(String response) {
if (mListener != null) {
mListener.onResponse(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));
}
}
通过HttpHeaderParser.parseCacheHeaders(response),可以得到Cache.Entry。
当Cache-Control是no-cache或者no-store时,不缓存响应;当Cache-Control是max-age时,缓存的过期时间softTtl/ttl= now + maxAge * 1000(优先);当没有设置Cache-Control,设置了资源的失效期Expires时,缓存的过期时间softTtl/ttl= now + (serverExpires - serverDate);
public class HttpHeaderParser {
public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
long now = System.currentTimeMillis();
Map<String, String> headers = response.headers;
long serverDate = 0;
long lastModified = 0;
long serverExpires = 0;
long softExpire = 0;
long finalExpire = 0;
long maxAge = 0;
long staleWhileRevalidate = 0;
boolean hasCacheControl = false;
boolean mustRevalidate = false;
String serverEtag = null;
String headerValue;
// 创建报文的日期
headerValue = headers.get("Date");
if (headerValue != null) {
serverDate = parseDateAsEpoch(headerValue);
}
//Cache-Control 控制缓存行为
//如果是no-cache或者no-store,不进行缓存
// 如果是max-age,取出max-age值,
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.startsWith("stale-while-revalidate=")) {
try {
staleWhileRevalidate = Long.parseLong(token.substring(23));
} catch (Exception e) {
}
} else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
mustRevalidate = true;
}
}
}
// 资源的失效期
headerValue = headers.get("Expires");
if (headerValue != null) {
serverExpires = parseDateAsEpoch(headerValue);
}
//资源上一次修改的时间
headerValue = headers.get("Last-Modified");
if (headerValue != null) {
lastModified = 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;
finalExpire = mustRevalidate
? softExpire
: softExpire + staleWhileRevalidate * 1000;
} else if (serverDate > 0 && serverExpires >= serverDate) {
// Default semantic for Expire header in HTTP specification is softExpire.
softExpire = now + (serverExpires - serverDate);
finalExpire = softExpire;
}
Cache.Entry entry = new Cache.Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = finalExpire;
entry.serverDate = serverDate;
entry.lastModified = lastModified;
entry.responseHeaders = headers;
return entry;
}
}
public interface Cache {
class Entry {
/** The data returned from cache. */
public byte[] data;
/** ETag for cache coherency. */
public String etag;
/** Date of this response as reported by the server. */
public long serverDate;
/** The last modified date for the requested object. */
public long lastModified;
/** TTL for this record. */
public long ttl;
/** Soft TTL for this record. */
public long softTtl;
/** Immutable response headers as received from server; must be non-null. */
public Map<String, String> responseHeaders = Collections.emptyMap();
/** True if the entry is expired. */
boolean isExpired() {
return this.ttl < System.currentTimeMillis();
}
/** True if a refresh is needed from the original data source. */
boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}
}
}
4、取消请求
将对应的request标记为canceled,在进行分发之前会判断,如果当前request已经被取消了,就不进行分发
RequestQueue
/**
* Cancels all requests in this queue for which the given filter applies.
* @param filter The filtering function to use
*/
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}
/**
* Cancels all requests in this queue with the given tag. Tag must be non-null
* and equality is by identity.
*/
public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> request) {
return request.getTag() == tag;
}
});
}
request
/**
* Mark this request as canceled. No callback will be delivered.
*/
public void cancel() {
mCanceled = true;
}
ExecutorDelivery
public class ExecutorDelivery implements ResponseDelivery {
...
/**
* A Runnable used for delivering network responses to a listener on the
* main thread.
*/
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
...
@Override
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
...
}
}
}
ExecutorDelivery
public class ExecutorDelivery implements ResponseDelivery {
/** Used for posting responses, typically to the main thread. */
private final Executor mResponsePoster;
public ExecutorDelivery(final Handler handler) {
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
@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));
}
/**
* A Runnable used for delivering network responses to a listener on the
* main thread.
*/
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) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
// If this request has canceled, finish it and don'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'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();
}
}
}
}
5、超时重试机制
1、有关超时重试的一些异常
- ConnectTimeoutException : 请求超时
A timeout while connecting to an HTTP server or waiting for an available connection from an HttpConnectionManager.
连接HTTP服务端超时或者等待HttpConnectionManager返回可用连接超时,俗称请求超时. - SocketTimeoutException : 响应超时
Signals that a timeout has occurred on a socket read or accept.
Socket通信超时,即从服务端读取数据时超时,俗称响应超时.
Volley就是通过捕捉这两个异常来进行超时重试的.
2、原理
如下图,Network类是一个死循环,只有请求成功或者抛出异常才能退出循环。因此如果捕捉到程序抛出SocketTimeoutException或者ConnectTimeoutException,并不会跳出循环,而是进入到attemptRetryOnException方法。如果attemptRetryOnException方法中没有抛出VolleyError异常,程序再次进入while循环,从而完成超时重试机制.



参考:
HTTP请求中的缓存(cache)机制
Google网络框架Volley的使用,Cache-Control=no-cache时强制缓存的处理
Volley在没有网的情况下使用磁盘缓存的数据
Android中关于Volley的使用(八)缓存机制的深入认识、volley超时重试机制
4、加载图片Request
- ImageRequest
适合请求图片比较少的情况
ImageRequest imageRequest = new ImageRequest(url, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
imageView.setImageBitmap(response);
}
}, maxWidth, maxHeight, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
imageView.setImageResource(R.drawable.ic_default_img);
}
});
注意:
其中maxWidth表示这个bitmap的最大宽度,0表示不设置,使用图片本来的宽度。
还有一种更简单的获取图片的方式:
ImageLoader imageLoader=new ImageLoader(queue, new ImageLoader.ImageCache() {
private final LruCache<String,Bitmap> lruCache=new LruCache<String,Bitmap>(8 * 1024 * 1024){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()*value.getHeight();
}
};
@Override
public Bitmap getBitmap(String url) {
return lruCache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
lruCache.put(url,bitmap);
}
});
ImageLoader.ImageListener listener=ImageLoader.getImageListener(imageView, R.drawable.ic_default_img,R.drawable.ic_default_img);
//maxWidth表示这个bitmap的最大宽度,0表示不设置,使用图片本来的宽度
imageLoader.get(url,listener,maxWidth,maxHeight);
注意:
1、ImageLoader非常适合加载大量图片的情况,它在正常的硬盘缓存之前使用了内存缓存(传入ImageCache),可以避免内存闪烁,实现了图片缓存的功能,同时还可以过滤重复链接,避免重复发送请求。
2、使用ImageLoader.getImageListener()方法创建一个ImageListener实例后,在imageLoader.get()方法中加入此监听器和图片的url,即可加载网络图片.
当然更简单的一种是使用:NetworkImageView
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/networkImageView"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_alignParentBottom="true"/>
---
ImageLoader imageLoader = new ImageLoader(...);
networkImageView.setDefaultImageResId(R.drawable.ic_default_img);
networkImageView.setErrorImageResId(R.drawable.ic_default_img);
networkImageView.setImageUrl(url, imageLoader);
注意:
NetworkImageView,在加载图片的时候它会自动获取自身的宽高,然后对比网络图片的宽度,再决定是否需要对图片进行压缩。也就是说,压缩过程是在内部完全自动化的,并不需要我们关心,NetworkImageView会始终呈现给我们一张大小刚刚好的网络图片,不会多占用任何一点内存。
当然了,如果你不想对图片进行压缩的话,其实也很简单,只需要在布局文件中把NetworkImageView的layout_width和layout_height都设置成wrap_content就可以了,这样NetworkImageView就会将该图片的原始大小展示出来,不会进行任何压缩。
参考:
Android Volley解析(一)之GET、POST请求篇
Android Volley完全解析
Android Volley框架的几种post提交请求方式
Android 文档
网友评论