一、前言

上篇文章我们分析了网络请求,这篇文章分析对图片的处理操作,如果没看上一篇,可以先看上一篇文章Volley 源码解析之网络请求。Volley 不仅仅对请求网络数据作了良好的封装,还封装了对图片的加载等操作,虽然比不上glidefresco ,不过可以满足我们的日常使用,从学习者的角度看看是怎么封装的。

二、简单使用

  1. 使用ImageRequest加载图片,用法跟请求网络的用法差不多,只是构造request的参数不太一样:

     String imageUrl = "https://pic1.zhimg.com/80/1a60ca062a1fe2f6d091cdd9749e9c68_hd.jpg";RequestQueue queue = Volley.newRequestQueue(this);ImageRequest imageRequest = new ImageRequest(imageUrl,response -> imageView.setImageBitmap(response),0, 0, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.ARGB_8888,error -> {});queue.add(imageRequest);
    复制代码
    • 第一个参数是图片地址没啥说的
    • 第二个参数是成功的回调,返回一个bitmap
    • 第三和第四个参数则是图片的最大的高度和宽度,0为默认图片大小,如果填写的图片最大的高度和宽度小于图片的实际尺寸则会进行压缩
    • 第五个值就是对图片进行边界缩放
    • 第六个参数是图片的格式,常用的就是RGB_565ARGB_8888,前者每个像素占2个字节,后者每个像素占4个字节,后者成像质量高,有alpha通道,如果使用的是jpg,不需要alpha通道则可以使用前者; 还有个ARGB_4444,不过已经废弃了,在4.4以后默认转成ARGB_8888ALPHA_8只有透明度,没有颜色值,一般很少使用
    • 最后个参数就是错误的回调
  2. 使用ImageLoader加载图片
    String imageUrl = "https://pic1.zhimg.com/80/1a60ca062a1fe2f6d091cdd9749e9c68_hd.jpg";
    RequestQueue queue = Volley.newRequestQueue(this);
    ImageLoader imageLoader = new ImageLoader(queue, new BitmapCache());
    ImageLoader.ImageListener imageListener = ImageLoader.getImageListener(imageView, R.mipmap.ic_launcher, R.mipmap.ic_launcher_round);
    imageLoader.get(imageUrl,imageListener, 0, 0);
    private class BitmapCache implements ImageLoader.ImageCache{private LruCache<String, Bitmap> lruCache;public BitmapCache() {int maxSize = 10 * 1024 * 1024;lruCache = new LruCache<String, Bitmap>(maxSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {return bitmap.getRowBytes() * bitmap.getHeight();}};}@Overridepublic Bitmap getBitmap(String url) {return lruCache.get(url);}@Overridepublic void putBitmap(String url, Bitmap bitmap) {lruCache.put(url, bitmap);}
    }
    复制代码
  3. 使用NetworkImageView加载图片
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:layout_marginTop="88dp"android:layout_marginEnd="8dp"android:text="Button"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><com.android.volley.toolbox.NetworkImageViewandroid:id="@+id/imageView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:layout_marginTop="32dp"android:layout_marginEnd="8dp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/textView"tools:srcCompat="@tools:sample/avatars" />
    </android.support.constraint.ConstraintLayout>
    复制代码
     String imageUrl = "https://pic1.zhimg.com/80/1a60ca062a1fe2f6d091cdd9749e9c68_hd.jpg";NetworkImageView networkImageView = findViewById(R.id.imageView);networkImageView.setDefaultImageResId(R.mipmap.ic_launcher);networkImageView.setErrorImageResId(R.mipmap.ic_launcher_round);networkImageView.setImageUrl(imageUrl, imageLoader);
    复制代码

三、源码分析

一、 ImageRequest 分析

首先我们分析ImageRequest,直接分析这个类,代码不多,直接继承Request,那么不用说跟上一篇我们分析的网络请求的request大体相同,不同的是这个是请求图片,如果我们需要自定义大小那么这里就对图片进行了裁剪以满足我们的大小:

public class ImageRequest extends Request<Bitmap> {//图片请求的超时时间,单位毫秒public static final int DEFAULT_IMAGE_TIMEOUT_MS = 1000;//图片请求的默认重试次数public static final int DEFAULT_IMAGE_MAX_RETRIES = 2;//发生冲突时的默认重传延迟增加数,和TCP协议有关系,退避算法,短时间的重复请求失败还会是失败public static final float DEFAULT_IMAGE_BACKOFF_MULT = 2f;//对mListener加锁,保证线程安全,避免取消的时候同时执行分发private final Object mLock = new Object();@GuardedBy("mLock")@Nullableprivate Response.Listener<Bitmap> mListener;private final Config mDecodeConfig;private final int mMaxWidth;private final int mMaxHeight;private final ScaleType mScaleType;//Bitmap 的同步解析锁,保证一个时间内只有一个Bitmap被加载到内存进行解析,避免多个同时解析oomprivate static final Object sDecodeLock = new Object();public ImageRequest(String url,Response.Listener<Bitmap> listener,int maxWidth,int maxHeight,ScaleType scaleType,Config decodeConfig,@Nullable Response.ErrorListener errorListener) {super(Method.GET, url, errorListener);setRetryPolicy(new DefaultRetryPolicy(DEFAULT_IMAGE_TIMEOUT_MS,DEFAULT_IMAGE_MAX_RETRIES,DEFAULT_IMAGE_BACKOFF_MULT));mListener = listener;mDecodeConfig = decodeConfig;mMaxWidth = maxWidth;mMaxHeight = maxHeight;mScaleType = scaleType;}@Deprecatedpublic ImageRequest(String url,Response.Listener<Bitmap> listener,int maxWidth,int maxHeight,Config decodeConfig,Response.ErrorListener errorListener) {this(url,listener,maxWidth,maxHeight,ScaleType.CENTER_INSIDE,decodeConfig,errorListener);}@Overridepublic Priority getPriority() {return Priority.LOW;}//根据ScaleType设置图片大小private static int getResizedDimension(int maxPrimary,int maxSecondary,int actualPrimary,int actualSecondary,ScaleType scaleType) {// 如果主要值和次要的值为0,就返回实际值,如果我们计算宽度的期望值,那么主要值就是宽度,高度就是次要值,反之亦然if ((maxPrimary == 0) && (maxSecondary == 0)) {return actualPrimary;}// 如果为ScaleType.FIT_XY,填充整个矩形,忽略比值;即如果主要的值为0则返回实际值,否则返回传入的值if (scaleType == ScaleType.FIT_XY) {if (maxPrimary == 0) {return actualPrimary;}return maxPrimary;}// 如果主要的值为0,则通过比例值计算出主要的值返回if (maxPrimary == 0) {double ratio = (double) maxSecondary / (double) actualSecondary;return (int) (actualPrimary * ratio);}// 次要的值为0,下面的比例调整就是多余的,那么直接返回主要的值,if (maxSecondary == 0) {return maxPrimary;}// 图片真实尺寸大小的比例,通过这个比例我们可以计算出次要的最大值,通过计算出的值和我们传递进来的值做比较double ratio = (double) actualSecondary / (double) actualPrimary;int resized = maxPrimary;// 如果是ScaleType.CENTER_CROP,填充整个矩形,保持长宽比,这里的宽高值相等或者大于传入的宽高尺寸if (scaleType == ScaleType.CENTER_CROP) {// 小于传入的次要最大值,则返回通过比例计算的最大值,这里相当于把resized 值增大if ((resized * ratio) < maxSecondary) {resized = (int) (maxSecondary / ratio);}return resized;}//  其它scaleType值,如果计算的值大于次要值,那么resized 值减小if ((resized * ratio) > maxSecondary) {resized = (int) (maxSecondary / ratio);}return resized;}//解析response@Overrideprotected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {synchronized (sDecodeLock) {try {return doParse(response);} catch (OutOfMemoryError e) {VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl());return Response.error(new ParseError(e));}}}//解析的地方private Response<Bitmap> doParse(NetworkResponse response) {byte[] data = response.data;BitmapFactory.Options decodeOptions = new BitmapFactory.Options();Bitmap bitmap = null;//如果最大宽度和最大高度都传入的为0,直接解析成一个bitmapif (mMaxWidth == 0 && mMaxHeight == 0) {decodeOptions.inPreferredConfig = mDecodeConfig;bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);} else {//如果要调整图片的大小,首先获取图片真实的尺寸大小,首先设置inJustDecodeBounds为true,不加载到内存但是可以获取图像的宽高decodeOptions.inJustDecodeBounds = true;BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);int actualWidth = decodeOptions.outWidth;int actualHeight = decodeOptions.outHeight;// 计算我们想要的尺寸int desiredWidth =getResizedDimension(mMaxWidth, mMaxHeight, actualWidth, actualHeight, mScaleType);int desiredHeight =getResizedDimension(mMaxHeight, mMaxWidth, actualHeight, actualWidth, mScaleType);// 计算出采样值,2的倍数decodeOptions.inJustDecodeBounds = false;decodeOptions.inSampleSize =findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);// 如果采样率计算出的值为1的话,那么就没有尺寸压缩,tempBitmap的宽高值就是图片的真实值,那么这里就需要缩放到满足我们上面计算出来的值if (tempBitmap != null&& (tempBitmap.getWidth() > desiredWidth|| tempBitmap.getHeight() > desiredHeight)) {bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true);tempBitmap.recycle();} else {bitmap = tempBitmap;}}//回调给用户if (bitmap == null) {return Response.error(new ParseError(response));} else {return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));}}@Overridepublic void cancel() {super.cancel();synchronized (mLock) {mListener = null;}}@Overrideprotected void deliverResponse(Bitmap response) {Response.Listener<Bitmap> listener;synchronized (mLock) {listener = mListener;}if (listener != null) {listener.onResponse(response);}}//计算合适的采样率@VisibleForTestingstatic int findBestSampleSize(int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {double wr = (double) actualWidth / desiredWidth;double hr = (double) actualHeight / desiredHeight;double ratio = Math.min(wr, hr);float n = 1.0f;while ((n * 2) <= ratio) {n *= 2;}return (int) n;}
}复制代码

关键部分我都写了注释,下面我们看主要流程,对 Bitmap的高效加载。首先我们获取到返回的response进行解析,然后根据传递的期望宽高以及图片格式生成Bitmap返回,对于我们传入的宽高会按比例裁剪,不是直接使用裁剪到合适的值,不然会有拉伸,最后再回调给用户。

二、ImageLoader 分析

我们直接先看构造方法,看所有的关键地方,不重要的就不分析了

public ImageLoader(RequestQueue queue, ImageCache imageCache) {mRequestQueue = queue;mCache = imageCache;
}
复制代码

没啥说的,就是赋值,一个是请求队列,一个是图片缓存的自己实现,这个是在内部把请求添加到请求队列,所以直接传递进去,第二个参数缓存,我们可以自己实现,一般使用LruCache实现。 接下来我们接着看getImageListener:

public static ImageListener getImageListener(final ImageView view, final int defaultImageResId, final int errorImageResId) {return new ImageListener() {@Overridepublic void onErrorResponse(VolleyError error) {if (errorImageResId != 0) {view.setImageResource(errorImageResId);}}@Overridepublic void onResponse(ImageContainer response, boolean isImmediate) {if (response.getBitmap() != null) {view.setImageBitmap(response.getBitmap());} else if (defaultImageResId != 0) {view.setImageResource(defaultImageResId);}}};
}
复制代码

这个方法比较简单,就是传入我们的image view进行设置图像,然后分别提供一个默认和请求失败的占位图,刚开始设置的时候还没有请求到Bitmap,所以最开始设置的事默认图。
首先看两个变量,后面需要用到:

//相同URL正在请求中存储的map
private final HashMap<String, BatchedImageRequest> mInFlightRequests = new HashMap<>();
//相同URL请求结果存储的map
private final HashMap<String, BatchedImageRequest> mBatchedResponses = new HashMap<>();
复制代码

接下来我们看看关键的一步

    @MainThreadpublic ImageContainer get(String requestUrl,ImageListener imageListener,int maxWidth,int maxHeight,ScaleType scaleType) {// 检查当前线程是否在主线程,只满足从主线程发起的请求Threads.throwIfNotOnMainThread();//根据url、width、height、scaleType拼接的缓存keyfinal String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);// 从缓存中查找bitmapBitmap cachedBitmap = mCache.getBitmap(cacheKey);if (cachedBitmap != null) {// 有相应的缓存那么则返回一个ImageContainer,包括其中的bitmapImageContainer container =new ImageContainer(cachedBitmap, requestUrl, /* cacheKey= */ null, /* listener= */ null);// 直接调用onResponse,把bitmap设置给imageViewimageListener.onResponse(container, true);return container;}// 缓存中没有查找到,那么我们直接获取,首先new一个ImageContainerImageContainer imageContainer =new ImageContainer(null, requestUrl, cacheKey, imageListener);// 更新调用的地方,使用默认的图片先设置imageListener.onResponse(imageContainer, true);//检查是否有相同的cacheKey请求正在运行BatchedImageRequest request = mInFlightRequests.get(cacheKey);if (request != null) {// 如果相同的请求正在运行,那么不需要重复请求,只需要将这个实例化的imageContainer添加到BatchedImageRequest的mContainers中,然后请求结束后对所有添加到集合中的imageContainer依次回调request.addContainer(imageContainer);return imageContainer;}// 如果这个请求尚未执行,发送一个新的请求到网络上,这里才是执行请求的地方Request<Bitmap> newRequest =makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType, cacheKey);//添加到请求队列mRequestQueue.add(newRequest);//添加到正在请求的集合中mInFlightRequests.put(cacheKey, new BatchedImageRequest(newRequest, imageContainer));return imageContainer;
}
复制代码

这个方法是一个重点,主要流程是首先看看缓存里面有没有缓存的Bitmap,来源于我们自己实现的缓存策略,我们使用的是内存缓存的话这里就是一级缓存;如果有直接调用onResponse方法设置图片,如果没有,首先实例化ImageContainer,涉及到了几个类,接下来就看是否有相同的请求,如果有则添加到一个集合中,请求下来统一处理;如果没有那么则构造一个Request,通过RequestQueue去获取网络图片,可能是网络请求也有可能是磁盘缓存的,这里就是二级缓存,然后添加到正在请求的集合中。
接下来看一看ImageContainer这个类,这个类就是图像请求的一个容器对象

public class ImageContainer {//imageview加载的Bitmapprivate Bitmap mBitmap;//图片加载成功和失败的监听private final ImageListener mListener;//缓存的keyprivate final String mCacheKey;//请求指定的URLprivate final String mRequestUrl;public ImageContainer(Bitmap bitmap, String requestUrl, String cacheKey, ImageListener listener) {mBitmap = bitmap;mRequestUrl = requestUrl;mCacheKey = cacheKey;mListener = listener;}//取消请求@MainThreadpublic void cancelRequest() {Threads.throwIfNotOnMainThread();if (mListener == null) {return;}//从正在请求的集合获取一个批处理requestBatchedImageRequest request = mInFlightRequests.get(mCacheKey);if (request != null) {//如果取到request,那么首先从mContainers移除当前的这个ImageContainer,如果移除后集合为空一个ImageContainer也没有了,那么则取消掉这个请求并返回 trueboolean canceled = request.removeContainerAndCancelIfNecessary(this);if (canceled) {//取消了请求则从正在请求的集合中移除BatchedImageRequestmInFlightRequests.remove(mCacheKey);}} else {// 如果已经请求完成添加到批处理中准备处理分发request = mBatchedResponses.get(mCacheKey);if (request != null) {//首先从mContainers移除当前的这个ImageContainerrequest.removeContainerAndCancelIfNecessary(this);if (request.mContainers.size() == 0) {//如果集合中一个ImageContainer都没有,则从等待处理的response中移除掉这个BatchedImageRequestmBatchedResponses.remove(mCacheKey);}}}}public Bitmap getBitmap() {return mBitmap;}/** Returns the requested URL for this container. */public String getRequestUrl() {return mRequestUrl;}}
复制代码

这个类主要包含是一个图片的容器对象,里面包括了bitmap、监听器、缓存的key以及请求的URL,每个请求都会先组装这个类,然后添加到一个BatchedImageRequest的mContainers中
接下来我们看看真正发起请求的地方:

protected Request<Bitmap> makeImageRequest(String requestUrl,int maxWidth,int maxHeight,ScaleType scaleType,final String cacheKey) {return new ImageRequest(requestUrl,new Listener<Bitmap>() {@Overridepublic void onResponse(Bitmap response) {onGetImageSuccess(cacheKey, response);}},maxWidth,maxHeight,scaleType,Config.RGB_565,new ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {onGetImageError(cacheKey, error);}});}//图片请求成功protected void onGetImageSuccess(String cacheKey, Bitmap response) {// 添加到以及缓存中mCache.putBitmap(cacheKey, response);// 从正在运行的请求列表中删除这个请求BatchedImageRequest request = mInFlightRequests.remove(cacheKey);if (request != null) {//更新BatchedImageRequest的bitmaprequest.mResponseBitmap = response;//发送一个批处理请求,将多个相同的请求进行分发batchResponse(cacheKey, request);}}//图片请求失败,跟上面成功处理大致类似protected void onGetImageError(String cacheKey, VolleyError error) {BatchedImageRequest request = mInFlightRequests.remove(cacheKey);if (request != null) {//设置这个请求的错误request.setError(error);batchResponse(cacheKey, request);}}
复制代码

这里执行网络请求还是调用我们上面分析的ImageRequest方法,而且在回调中分别对成功和失败在进行了一次处理。
接下来我们看看这个批量处理图片的batchResponse方法:

private void batchResponse(String cacheKey, BatchedImageRequest request) {//首先添加到这个map中,表明现在进入了批处理中mBatchedResponses.put(cacheKey, request);// 如果还没有进行处理,那么我们则开始一个新的任务if (mRunnable == null) {mRunnable =new Runnable() {@Overridepublic void run() {//循环mBatchedResponses的所有值for (BatchedImageRequest bir : mBatchedResponses.values()) {//循环BatchedImageRequest的mContainers的值for (ImageContainer container : bir.mContainers) {//如果有的请求取消了,在收到请求的响应后还没有分发之前那么跳过循环下一个if (container.mListener == null) {continue;}// 如果不是请求错误则调用onResponseif (bir.getError() == null) {container.mBitmap = bir.mResponseBitmap;container.mListener.onResponse(container, false);} else {//请求报错则调用onErrorResponse设置一个错误的图片展示 container.mListener.onErrorResponse(bir.getError());}}}//清除所有响应的BatchedImageRequestmBatchedResponses.clear();//置为null,通过是否为null判断当前是否正在处理mRunnable = null;}};// 将这个post投递到主线程去执行mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);}}
复制代码

这段代码也很简单,不过有个地方有个比较奇怪的地方,为啥是使用双层循环,为啥不直接使用内层的循环;个人认为有可能是这样,首先这个mBatchedResponses刚开始进来添加了相同的key的请求的BatchedImageRequest,那么存在正在分发的时候又有不同的key的请求进来了,因为正在处理的时候runnable不为null,则后续添加的有可能不能分发,所以要遍历这个map中所有的请求。

三、 NetworkImageView 分析

这是一个继承ImageView的自定义view

public class NetworkImageView extends ImageView {private String mUrl;//设置默认的图片private int mDefaultImageId;//设置请求错误的时候显示的图片private int mErrorImageId;private ImageLoader mImageLoader;private ImageContainer mImageContainer;public NetworkImageView(Context context) {this(context, null);}public NetworkImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public NetworkImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}//这个方法就是设置我们的url@MainThreadpublic void setImageUrl(String url, ImageLoader imageLoader) {Threads.throwIfNotOnMainThread();mUrl = url;mImageLoader = imageLoader;// 我们的url可能已经更改,那么我们则需要判断是否需要加载loadImageIfNecessary(/* isInLayoutPass= */ false);}public void setDefaultImageResId(int defaultImage) {mDefaultImageId = defaultImage;}public void setErrorImageResId(int errorImage) {mErrorImageId = errorImage;}//如果视图尚未加载图像,那么我们则去加载它void loadImageIfNecessary(final boolean isInLayoutPass) {int width = getWidth();int height = getHeight();ScaleType scaleType = getScaleType();boolean wrapWidth = false, wrapHeight = false;if (getLayoutParams() != null) {wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT;wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT;}//如果不知道视图的大小并且不是WRAP_CONTENT就暂停加载boolean isFullyWrapContent = wrapWidth && wrapHeight;if (width == 0 && height == 0 && !isFullyWrapContent) {return;}// 如果url为空的话则请取消所有的请求,包括以前的请求,假如请求两次最后次的url为null,这时候还没请求完成,肯定以最后次为准if (TextUtils.isEmpty(mUrl)) {if (mImageContainer != null) {mImageContainer.cancelRequest();mImageContainer = null;}//设置默认的图片setDefaultImageOrNull();return;}// 检查是否取消以前的请求if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {if (mImageContainer.getRequestUrl().equals(mUrl)) {// 如果请求和以前相同则没必要再次请求return;} else {// 如果存在正在请求的url并且请求url不同,那么取消正在请求的urlmImageContainer.cancelRequest();setDefaultImageOrNull();}}// 计算最大宽高,如果设置WRAP_CONTENT那么则图片是多大就是多大,其它情况则直接使用布局的宽高,如果设置了具体的值就有可能裁剪int maxWidth = wrapWidth ? 0 : width;int maxHeight = wrapHeight ? 0 : height;// 使用ImageLoader来请求图像,上面已经分析了,最终返回一个ImageContainermImageContainer =mImageLoader.get(mUrl,new ImageListener() {@Overridepublic void onErrorResponse(VolleyError error) {if (mErrorImageId != 0) {setImageResource(mErrorImageId);}}@Overridepublic void onResponse(final ImageContainer response, boolean isImmediate) {//isImmediate:在网络请求过程中调用的时候为true,可以用来区分是否是取的缓存图像还是网络图像加载isInLayoutPass:如果通过onLayout调用此函数,则为true,否则为false这个if的意思就是,如果是缓存图像并且是在布局中调用那么则发送到主线程并延迟设置图像,因为可能多次调用if (isImmediate && isInLayoutPass) {post(new Runnable() {@Overridepublic void run() {onResponse(response, /* isImmediate= */ false);}});return;}//请求成功加载图片if (response.getBitmap() != null) {setImageBitmap(response.getBitmap());} else if (mDefaultImageId != 0) {setImageResource(mDefaultImageId);}}},maxWidth,maxHeight,scaleType);}private void setDefaultImageOrNull() {if (mDefaultImageId != 0) {setImageResource(mDefaultImageId);} else {setImageBitmap(null);}}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);loadImageIfNecessary(/* isInLayoutPass= */ true);}//当imageview销毁的时候,取消请求并且清除ImageContainer以便重新加载图像@Overrideprotected void onDetachedFromWindow() {if (mImageContainer != null) {// If the view was bound to an image request, cancel it and clear// out the image from the view.mImageContainer.cancelRequest();setImageBitmap(null);// also clear out the container so we can reload the image if necessary.mImageContainer = null;}super.onDetachedFromWindow();}@Overrideprotected void drawableStateChanged() {super.drawableStateChanged();invalidate();}
}复制代码

这个类没啥好分析的,就是利用前两个类来完成请求,只不过方便的是直接在xml中使用,使用ImageLoader请求的Bitmap设置给NetworkImageView

四、总结

三种不同的方式都可以完成图片的加载,不过后面的方式都比较依赖前面的ImageRequest,毕竟还是要这个类去完成网络请求操作;在使用中,根据不同的场景选择不同的方式使用。不过我建议使用ImageLoader来加载图片,可以自己设置缓存,两级缓存,一级内存缓存,一级volley请求时候的磁盘缓存。总体来讲封装的很不错,对一些细节处理的比较好,比如相同的请求、图片的裁剪等,值得我们学习的地方很多。

参考

Android Volley完全解析(二),使用Volley加载网络图片
Volley 源码解析

转载于:https://juejin.im/post/5c1c84ade51d4535f05a192c

Volley 源码解析之图片请求相关推荐

  1. Volley 源码解析之网络请求

    Volley源码分析三部曲 Volley 源码解析之网络请求 Volley 源码解析之图片请求 Volley 源码解析之缓存机制 Volley 是 Google 推出的一款网络通信框架,非常适合数据量 ...

  2. Android xUtils3源码解析之图片模块

    本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...

  3. http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

    http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

  4. Android之Volley 源码解析

    原文来自:http://www.codekk.com  1. 功能介绍  1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Googl ...

  5. okhttp的应用详解与源码解析--android网络请求框架发展史

    乘5G之势,借物联网之风,Android未来亦可期,Android优势在于开放,手机.平板.车载设备.智能家居等都是Android的舞台,Google不倒,Android不灭,本专栏的同步视频教程已经 ...

  6. 源码解析-Volley(转自codeKK)

    Volley 源码解析 本文为 Android 开源项目源码解析 中 Volley 部分 项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo 分析者:grumo ...

  7. Spring 注解面面通 之 @CrossOrigin 处理请求源码解析

      @CrossOrigin源码解析主要分为两个阶段:   ① @CrossOrigin注释的方法扫描注册.   ② 请求匹配@CrossOrigin注释的方法.   本文针对第②阶段从源码角度进行解 ...

  8. OkHttp3源码解析(三)——连接池复用

    OKHttp3源码解析系列 OkHttp3源码解析(一)之请求流程 OkHttp3源码解析(二)--拦截器链和缓存策略 本文基于OkHttp3的3.11.0版本 implementation 'com ...

  9. Swift - Alamofire源码解析

    文章目录 Alamofire源码解析 一.Alamofire目录结构 二.使用的基本流程 (不讲解基本使用,从源码解析) 1. 发起请求的源码 (举例:普通请求流程) 2. SessionManage ...

最新文章

  1. linux成长笔录——week_1
  2. 解决android应用程序适用新老android系统版本方法
  3. java j集合_JNotes/Java-集合篇(2)集合之List.md at master · harryjudy2240/JNotes · GitHub...
  4. 如何在CDN边缘节点执行你的JavaScript?
  5. Gartner:2019年公有云服务六大趋势
  6. java中后退键_java - 单击后退按钮两次以退出活动
  7. 3、Linux多线程,线程同步(转)
  8. 基于JAVA+Spring+MYSQL的电影票预定系统
  9. 用户注册与登陆(验证和数据库)
  10. 让你的博文自动带上缩址,方便发到微博客上
  11. js与java对json的操作
  12. 车联网相关知识点整理
  13. DNS劫持新方法(“侧信道攻击“攻破“端口随机化“的研究)
  14. 宝塔linux 屏蔽ip,宝塔屏蔽国外IP保护网站安全
  15. 几种常见的4K高清视频信号传输方案对比
  16. 为什么孩子上学越来越难了?
  17. 【开发】后端框架——Mybatis
  18. AFNetwork 2.0在请求时报错code=-1016 和 3840
  19. 使用gdb调试Android(aarch 64)可执行二进制文件
  20. Linux之《荒岛余生》(三)内存篇

热门文章

  1. 【方法整理】Oracle 获取trace跟踪文件名的几种常用方式
  2. 前端小笔记:左定宽,右随意
  3. install tabix/bgzip
  4. 小强的HTML5移动开发之路(6)——Canvas图形绘制基础
  5. lnmp下安装PECL HTTP 扩展
  6. 以Dapper、Zipkin和LightStep [x]PM为例阐述分布式跟踪的过去、现在和未来
  7. Java 定时任务调度工具 Quartz(Part 2)
  8. 思科高级路由与交换(CISCO 部分) 第5天
  9. Python代码运行助手
  10. Unix调试的瑞士军刀:lsof