Android开发Android知识Android技术知识

Volley学习

2016-08-30  本文已影响369人  i冰点

1、基本使用

Volley适合轻量、高并发的网络请求,但如果大数据量的操作,比如上传下载文件,就不太适合了。使用Volley主要有以下几个步奏:

1、设置、启动请求队列RequestQueue
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、取消请求
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);
connection.setDoInput(true);
 connection.setDoOutput(true);
 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();

参考:HttpURLConnection用法详解

3、分析

life of request
1、网络请求

//Volley是如何设置请求报文(报文首部、报文主体)?响应报文是如何被缓存的?

Volley的网络请求从NetworkDispatcher开始,经过mNetwork(BasicNetwork)的performRequest,最终由HttpStack(HurlStack)的performRequest执行具体的网络请求并处理响应。然后通过request.parseNetworkResponse(),解析响应的数据。最后调用mDelivery,将结果分发到主线程。在这个过程中会根据条件,决定是否解析和分发数据,是否缓存数据

1、NetworkDispatcher主要做了下面几件事:


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

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


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、缓存

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、有关超时重试的一些异常

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

2、原理
如下图,Network类是一个死循环,只有请求成功或者抛出异常才能退出循环。因此如果捕捉到程序抛出SocketTimeoutException或者ConnectTimeoutException,并不会跳出循环,而是进入到attemptRetryOnException方法。如果attemptRetryOnException方法中没有抛出VolleyError异常,程序再次进入while循环,从而完成超时重试机制.

hurlStack,进行具体的网络请求 Network retry方法

参考:
HTTP请求中的缓存(cache)机制
Google网络框架Volley的使用,Cache-Control=no-cache时强制缓存的处理
Volley在没有网的情况下使用磁盘缓存的数据
Android中关于Volley的使用(八)缓存机制的深入认识volley超时重试机制

4、加载图片Request

    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 文档

上一篇 下一篇

猜你喜欢

热点阅读