博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android-Universal-Image-Loader完全解析---加载流程
阅读量:4941 次
发布时间:2019-06-11

本文共 27102 字,大约阅读时间需要 90 分钟。

我们拿最常用的函数来分析:

总体概况包含三个过程:保存图片,加载图片,显示图片。

displayImage(String uri, ImageView imageView)

所有的displayImage函数都是重写的下面的函数(参数最多)

public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,			ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {		checkConfiguration();		if (imageAware == null) {			throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);		}		if (listener == null) {			listener = defaultListener;		}		if (options == null) {			options = configuration.defaultDisplayImageOptions;		}		if (TextUtils.isEmpty(uri)) {			engine.cancelDisplayTaskFor(imageAware);			listener.onLoadingStarted(uri, imageAware.getWrappedView());			if (options.shouldShowImageForEmptyUri()) {				imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));			} else {				imageAware.setImageDrawable(null);			}			listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);			return;		}		if (targetSize == null) {			targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());		}		String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);		engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);		listener.onLoadingStarted(uri, imageAware.getWrappedView());		Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);		if (bmp != null && !bmp.isRecycled()) {			L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);			if (options.shouldPostProcess()) {				ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,						options, listener, progressListener, engine.getLockForUri(uri));				ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,						defineHandler(options));				if (options.isSyncLoading()) {					displayTask.run();				} else {					engine.submit(displayTask);				}			} else {				options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);				listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);			}		} else {			if (options.shouldShowImageOnLoading()) {				imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));			} else if (options.isResetViewBeforeLoading()) {				imageAware.setImageDrawable(null);			}			ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,					options, listener, progressListener, engine.getLockForUri(uri));			LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,					defineHandler(options));			if (options.isSyncLoading()) {				displayTask.run();			} else {				engine.submit(displayTask);			}		}	}

一.分析入参

* @param uri              Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageAware       {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} *                         which should display image * @param options          {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image *                         decoding and displaying. If null - default display image options *                         {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) *                         from configuration} will be used. * @param targetSize       {@linkplain ImageSize} Image target size. If null - size will depend on the view * @param listener         {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires *                         events on UI thread if this method is called on UI thread. * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener *                         Listener} for image loading progress. Listener fires events on UI thread if this method *                         is called on UI thread. Caching on disk should be enabled in *                         {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make *                         this listener work.

uri:Image路径,支持网络路径,本地路径,drawable路径等

imageAware:Bitmap显示的容器,这个类是个接口,扩展性极好,凡是实现接口的容器ImageLoader都可以支持,不只局限于ImageVIew

public interface ImageAware {		int getWidth();		int getHeight();	ViewScaleType getScaleType();		View getWrappedView();	boolean isCollected();	int getId();	boolean setImageDrawable(Drawable drawable);	boolean setImageBitmap(Bitmap bitmap);}

  displayImage(String uri, ImageView imageView)参数ImageView是如何转化成ImageAware的?

public void displayImage(String uri, ImageView imageView) {		displayImage(uri, new ImageViewAware(imageView), null, null, null);}

  看下ImageViewAware的实现

public class ImageViewAware extends ViewAware {	/**	 * Constructor. 
* References {@link #ImageViewAware(android.widget.ImageView, boolean) ImageViewAware(imageView, true)}. * * @param imageView {@link android.widget.ImageView ImageView} to work with */ public ImageViewAware(ImageView imageView) { super(imageView); } /** * Constructor * * @param imageView {@link android.widget.ImageView ImageView} to work with * @param checkActualViewSize true - then {@link #getWidth()} and {@link #getHeight()} will check actual * size of ImageView. It can cause known issues like * this. * But it helps to save memory because memory cache keeps bitmaps of actual (less in * general) size. *

* false - then {@link #getWidth()} and {@link #getHeight()} will NOT * consider actual size of ImageView, just layout parameters.
If you set 'false' * it's recommended 'android:layout_width' and 'android:layout_height' (or * 'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to * save memory. *

*/ public ImageViewAware(ImageView imageView, boolean checkActualViewSize) { super(imageView, checkActualViewSize); } /** * {@inheritDoc} *
* 3) Get maxWidth. */ @Override public int getWidth() { int width = super.getWidth(); if (width <= 0) { ImageView imageView = (ImageView) viewRef.get(); if (imageView != null) { width = getImageViewFieldValue(imageView, "mMaxWidth"); // Check maxWidth parameter } } return width; } /** * {@inheritDoc} *
* 3) Get maxHeight */ @Override public int getHeight() { int height = super.getHeight(); if (height <= 0) { ImageView imageView = (ImageView) viewRef.get(); if (imageView != null) { height = getImageViewFieldValue(imageView, "mMaxHeight"); // Check maxHeight parameter } } return height; } @Override public ViewScaleType getScaleType() { ImageView imageView = (ImageView) viewRef.get(); if (imageView != null) { return ViewScaleType.fromImageView(imageView); } return super.getScaleType(); } @Override public ImageView getWrappedView() { return (ImageView) super.getWrappedView(); } @Override protected void setImageDrawableInto(Drawable drawable, View view) { ((ImageView) view).setImageDrawable(drawable); if (drawable instanceof AnimationDrawable) { ((AnimationDrawable)drawable).start(); } } @Override protected void setImageBitmapInto(Bitmap bitmap, View view) { ((ImageView) view).setImageBitmap(bitmap); } private static int getImageViewFieldValue(Object object, String fieldName) { int value = 0; try { Field field = ImageView.class.getDeclaredField(fieldName); field.setAccessible(true); int fieldValue = (Integer) field.get(object); if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) { value = fieldValue; } } catch (Exception e) { L.e(e); } return value; }}

  ViewAware的实现

public abstract class ViewAware implements ImageAware {	public static final String WARN_CANT_SET_DRAWABLE = "Can't set a drawable into view. You should call ImageLoader on UI thread for it.";	public static final String WARN_CANT_SET_BITMAP = "Can't set a bitmap into view. You should call ImageLoader on UI thread for it.";	protected Reference
viewRef; protected boolean checkActualViewSize; /** * Constructor.
* References {@link #ViewAware(android.view.View, boolean) ImageViewAware(imageView, true)}. * * @param view {@link android.view.View View} to work with */ public ViewAware(View view) { this(view, true); } /** * Constructor * * @param view {@link android.view.View View} to work with * @param checkActualViewSize
true - then {@link #getWidth()} and {@link #getHeight()} will check actual * size of View. It can cause known issues like *
this. * But it helps to save memory because memory cache keeps bitmaps of actual (less in * general) size. *

*
false - then {@link #getWidth()} and {@link #getHeight()} will
NOT * consider actual size of View, just layout parameters.
If you set 'false' * it's recommended 'android:layout_width' and 'android:layout_height' (or * 'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to * save memory. */ public ViewAware(View view, boolean checkActualViewSize) { if (view == null) throw new IllegalArgumentException("view must not be null"); this.viewRef = new WeakReference
(view); this.checkActualViewSize = checkActualViewSize; } /** * {@inheritDoc} *

* Width is defined by target {@link android.view.View view} parameters, configuration * parameters or device display dimensions.
* Size computing algorithm (go by steps until get non-zero value):
* 1) Get the actual drawn
getWidth() of the View
* 2) Get
layout_width */ @Override public int getWidth() { View view = viewRef.get(); if (view != null) { final ViewGroup.LayoutParams params = view.getLayoutParams(); int width = 0; if (checkActualViewSize && params != null && params.width != ViewGroup.LayoutParams.WRAP_CONTENT) { width = view.getWidth(); // Get actual image width } if (width <= 0 && params != null) width = params.width; // Get layout width parameter return width; } return 0; } /** * {@inheritDoc} *

* Height is defined by target {@link android.view.View view} parameters, configuration * parameters or device display dimensions.
* Size computing algorithm (go by steps until get non-zero value):
* 1) Get the actual drawn
getHeight() of the View
* 2) Get
layout_height */ @Override public int getHeight() { View view = viewRef.get(); if (view != null) { final ViewGroup.LayoutParams params = view.getLayoutParams(); int height = 0; if (checkActualViewSize && params != null && params.height != ViewGroup.LayoutParams.WRAP_CONTENT) { height = view.getHeight(); // Get actual image height } if (height <= 0 && params != null) height = params.height; // Get layout height parameter return height; } return 0; } @Override public ViewScaleType getScaleType() { return ViewScaleType.CROP; } @Override public View getWrappedView() { return viewRef.get(); } @Override public boolean isCollected() { return viewRef.get() == null; } @Override public int getId() { View view = viewRef.get(); return view == null ? super.hashCode() : view.hashCode(); } @Override public boolean setImageDrawable(Drawable drawable) { if (Looper.myLooper() == Looper.getMainLooper()) { View view = viewRef.get(); if (view != null) { setImageDrawableInto(drawable, view); return true; } } else { L.w(WARN_CANT_SET_DRAWABLE); } return false; } @Override public boolean setImageBitmap(Bitmap bitmap) { if (Looper.myLooper() == Looper.getMainLooper()) { View view = viewRef.get(); if (view != null) { setImageBitmapInto(bitmap, view); return true; } } else { L.w(WARN_CANT_SET_BITMAP); } return false; } /** * Should set drawable into incoming view. Incoming view is guaranteed not null.
* This method is called on UI thread. */ protected abstract void setImageDrawableInto(Drawable drawable, View view); /** * Should set Bitmap into incoming view. Incoming view is guaranteed not null.< br /> * This method is called on UI thread. */ protected abstract void setImageBitmapInto(Bitmap bitmap, View view);}

  其中 boolean isCollected() 是否被gc回收 注意实现函数。

options参数:显示的一些配置,后续会统一讲解

imageSize参数:加载图片最大宽高

ImageLoadingListener,ImageLoadingProgressListener加载过程中的监听器

二.加载过程(保存,加载,显示)

1.一些参数的校验和初始化

其中:ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());  

targetSize(最大宽高)如果为空会取容器的宽高(避免图片太大内存溢出)。

2.接下来开始加载图片。

首先由路径生成内存缓存的唯一Key值memoryCacheKey ,且缓存起来。格式:[imageUri]_[width]x[height] 由此可见同一图片不同宽高缓存的是两个Key。

接着根据key获取缓存中的bitmap值。

如果本地缓存有则直接显示 其中 options.shouldPostProcess()==true表示后期需要Bitmap处理的情况,具体Bitmap处理逻辑需要配置 BitmapProcessor postProcessor去处理,实现 process方法,如切图等等耗时操作放在线程中去处理,此类如下:

public interface BitmapProcessor {	/**	 * Makes some processing of incoming bitmap.
* This method is executing on additional thread (not on UI thread).
* Note: If this processor is used as {@linkplain DisplayImageOptions.Builder#preProcessor(BitmapProcessor) * pre-processor} then don't forget {@linkplain Bitmap#recycle() to recycle} incoming bitmap if you return a new * created one. * * @param bitmap Original {@linkplain Bitmap bitmap} * @return Processed {@linkplain Bitmap bitmap} */ Bitmap process(Bitmap bitmap);}

 如果无本地缓存去加载,其中如果配置加载图片则显示 

imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));

接着我们分析比较关键在加载代码:

ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,					options, listener, progressListener, engine.getLockForUri(uri));			LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,defineHandler(options));			if (options.isSyncLoading()) {				displayTask.run();			} else {				engine.submit(displayTask);			}

 engine.submit 就是线程池中加入线程异步处理,如果配置不限制线程数量默认最大线程数。我们直接看run方法(图片加载过程)

public void run() {		if (waitIfPaused()) return;		if (delayIfNeed()) return;		ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;		L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);		if (loadFromUriLock.isLocked()) {			L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);		}		loadFromUriLock.lock();		Bitmap bmp;		try {			checkTaskNotActual();			bmp = configuration.memoryCache.get(memoryCacheKey);			if (bmp == null || bmp.isRecycled()) {				bmp = tryLoadBitmap();				if (bmp == null) return; // listener callback already was fired				checkTaskNotActual();				checkTaskInterrupted();				if (options.shouldPreProcess()) {					L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey);					bmp = options.getPreProcessor().process(bmp);					if (bmp == null) {						L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey);					}				}				if (bmp != null && options.isCacheInMemory()) {					L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);					configuration.memoryCache.put(memoryCacheKey, bmp);				}			} else {				loadedFrom = LoadedFrom.MEMORY_CACHE;				L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);			}			if (bmp != null && options.shouldPostProcess()) {				L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey);				bmp = options.getPostProcessor().process(bmp);				if (bmp == null) {					L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);				}			}			checkTaskNotActual();			checkTaskInterrupted();		} catch (TaskCancelledException e) {			fireCancelEvent();			return;		} finally {			loadFromUriLock.unlock();		}		DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);		runTask(displayBitmapTask, syncLoading, handler, engine);	}

if (waitIfPaused()) return; 如果当前暂停加载无限期wait,开关在ImageLoader的pause()和resume()方法,比如快速滑动时暂停加载,停止滑动时恢复

if (delayIfNeed()) return; 延迟显示选项 开关和延迟时间配置在 option里

ReentrantLock loadFromUriLock 用到了同步锁 同步锁的创建在 ImageLoaderEngine中

ReentrantLock getLockForUri(String uri) {		ReentrantLock lock = uriLocks.get(uri);		if (lock == null) {			lock = new ReentrantLock();			uriLocks.put(uri, lock);		}		return lock;	}

可以看到同一路径bitmap的加载需要同步操作

接着看,从缓存中取缓存没有执行tryLoadBitmap加载Bitmap文件,看 tryLoadBitmap 源码如下:

从本地磁盘找如果找到去解析(加载本地文件),找不到执行缓存到本地磁盘操作(存文件)

private Bitmap tryLoadBitmap() throws TaskCancelledException {		Bitmap bitmap = null;		try {			File imageFile = configuration.diskCache.get(uri);			if (imageFile != null && imageFile.exists() && imageFile.length() > 0) {				L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey);				loadedFrom = LoadedFrom.DISC_CACHE;				checkTaskNotActual();				bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));			}			if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {				L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);				loadedFrom = LoadedFrom.NETWORK;				String imageUriForDecoding = uri;				if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {					imageFile = configuration.diskCache.get(uri);					if (imageFile != null) {						imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());					}				}				checkTaskNotActual();				bitmap = decodeImage(imageUriForDecoding);				if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {					fireFailEvent(FailType.DECODING_ERROR, null);				}			}		} catch (IllegalStateException e) {			fireFailEvent(FailType.NETWORK_DENIED, null);		} catch (TaskCancelledException e) {			throw e;		} catch (IOException e) {			L.e(e);			fireFailEvent(FailType.IO_ERROR, e);		} catch (OutOfMemoryError e) {			L.e(e);			fireFailEvent(FailType.OUT_OF_MEMORY, e);		} catch (Throwable e) {			L.e(e);			fireFailEvent(FailType.UNKNOWN, e);		}		return bitmap;	}

 如何存文件? tryCacheImageOnDisk

private boolean tryCacheImageOnDisk() throws TaskCancelledException {		L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey);		boolean loaded;		try {			loaded = downloadImage();			if (loaded) {				int width = configuration.maxImageWidthForDiskCache;				int height = configuration.maxImageHeightForDiskCache;				if (width > 0 || height > 0) {					L.d(LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey);					resizeAndSaveImage(width, height); // TODO : process boolean result				}			}		} catch (IOException e) {			L.e(e);			loaded = false;		}		return loaded;	}
将路径uri直接转流存文件 downloadImage
private boolean downloadImage() throws IOException {		InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());		if (is == null) {			L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey);			return false;		} else {			try {				return configuration.diskCache.save(uri, is, this);			} finally {				IoUtils.closeSilently(is);			}		}	}

 其中 resizeAndSaveImage 是什么意思? 大图片超过全局配置的最大宽高,进行压缩保存覆盖,节省空间

/** Decodes image file into Bitmap, resize it and save it back */	private boolean resizeAndSaveImage(int maxWidth, int maxHeight) throws IOException {		// Decode image file, compress and re-save it		boolean saved = false;		File targetFile = configuration.diskCache.get(uri);		if (targetFile != null && targetFile.exists()) {			ImageSize targetImageSize = new ImageSize(maxWidth, maxHeight);			DisplayImageOptions specialOptions = new DisplayImageOptions.Builder().cloneFrom(options)					.imageScaleType(ImageScaleType.IN_SAMPLE_INT).build();			ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey,					Scheme.FILE.wrap(targetFile.getAbsolutePath()), uri, targetImageSize, ViewScaleType.FIT_INSIDE,					getDownloader(), specialOptions);			Bitmap bmp = decoder.decode(decodingInfo);			if (bmp != null && configuration.processorForDiskCache != null) {				L.d(LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK, memoryCacheKey);				bmp = configuration.processorForDiskCache.process(bmp);				if (bmp == null) {					L.e(ERROR_PROCESSOR_FOR_DISK_CACHE_NULL, memoryCacheKey);				}			}			if (bmp != null) {				saved = configuration.diskCache.save(uri, bmp);				bmp.recycle();			}		}		return saved;	}
从缓存中存在文件或者保存完文件会执行加载bitmap过程,bitmap = decodeImage(imageUriForDecoding);
private Bitmap decodeImage(String imageUri) throws IOException {		ViewScaleType viewScaleType = imageAware.getScaleType();		ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,				getDownloader(), options);		return decoder.decode(decodingInfo);	}

 ImageDecoder 是给接口 默认用的是 BaseImageDecoder,我们看下 decode 方法

public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {		Bitmap decodedBitmap;		ImageFileInfo imageInfo;		InputStream imageStream = getImageStream(decodingInfo);		if (imageStream == null) {			L.e(ERROR_NO_IMAGE_STREAM, decodingInfo.getImageKey());			return null;		}		try {			imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo);			imageStream = resetStream(imageStream, decodingInfo);			Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo);			decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions);		} finally {			IoUtils.closeSilently(imageStream);		}		if (decodedBitmap == null) {			L.e(ERROR_CANT_DECODE_IMAGE, decodingInfo.getImageKey());		} else {			decodedBitmap = considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation,					imageInfo.exif.flipHorizontal);		}		return decodedBitmap;	}

获取输入流

defineImageSizeAndRotation解析旋转角度,如对于相册中的图片(ExifInfo对象)

resetStream初始化流

considerExactScaleAndOrientatiton 根据各种配置缩放图片,旋转图片

protected Bitmap considerExactScaleAndOrientatiton(Bitmap subsampledBitmap, ImageDecodingInfo decodingInfo,			int rotation, boolean flipHorizontal) {		Matrix m = new Matrix();		// Scale to exact size if need		ImageScaleType scaleType = decodingInfo.getImageScaleType();		if (scaleType == ImageScaleType.EXACTLY || scaleType == ImageScaleType.EXACTLY_STRETCHED) {			ImageSize srcSize = new ImageSize(subsampledBitmap.getWidth(), subsampledBitmap.getHeight(), rotation);			float scale = ImageSizeUtils.computeImageScale(srcSize, decodingInfo.getTargetSize(), decodingInfo					.getViewScaleType(), scaleType == ImageScaleType.EXACTLY_STRETCHED);			if (Float.compare(scale, 1f) != 0) {				m.setScale(scale, scale);				if (loggingEnabled) {					L.d(LOG_SCALE_IMAGE, srcSize, srcSize.scale(scale), scale, decodingInfo.getImageKey());				}			}		}		// Flip bitmap if need		if (flipHorizontal) {			m.postScale(-1, 1);			if (loggingEnabled) L.d(LOG_FLIP_IMAGE, decodingInfo.getImageKey());		}		// Rotate bitmap if need		if (rotation != 0) {			m.postRotate(rotation);			if (loggingEnabled) L.d(LOG_ROTATE_IMAGE, rotation, decodingInfo.getImageKey());		}		Bitmap finalBitmap = Bitmap.createBitmap(subsampledBitmap, 0, 0, subsampledBitmap.getWidth(), subsampledBitmap				.getHeight(), m, true);		if (finalBitmap != subsampledBitmap) {			subsampledBitmap.recycle();		}		return finalBitmap;	}

  

3.最后显示图片

DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);

runTask(displayBitmapTask, syncLoading, handler, engine); 

保证在UI线程中执行

static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) {		if (sync) {			r.run();		} else if (handler == null) {			engine.fireCallback(r);		} else {			handler.post(r);		}	}

  看显示task的实现 displayBitmapTask.run

public void run() {		if (imageAware.isCollected()) {			L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);			listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());		} else if (isViewWasReused()) {			L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);			listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());		} else {			L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);			displayer.display(bitmap, imageAware, loadedFrom);			engine.cancelDisplayTaskFor(imageAware);			listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);		}	}

  BitmapDisplayer 是个接口,可以各种扩展显示不同样式

public interface BitmapDisplayer {	/**	 * Displays bitmap in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}.	 * NOTE: This method is called on UI thread so it's strongly recommended not to do any heavy work in it.	 *	 * @param bitmap     Source bitmap	 * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} to	 *                   display Bitmap	 * @param loadedFrom Source of loaded image	 */	void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom);}

  如自带的实现圆角效果

public class RoundedBitmapDisplayer implements BitmapDisplayer {	protected final int cornerRadius;	protected final int margin;	public RoundedBitmapDisplayer(int cornerRadiusPixels) {		this(cornerRadiusPixels, 0);	}	public RoundedBitmapDisplayer(int cornerRadiusPixels, int marginPixels) {		this.cornerRadius = cornerRadiusPixels;		this.margin = marginPixels;	}	@Override	public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {		if (!(imageAware instanceof ImageViewAware)) {			throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");		}		imageAware.setImageDrawable(new RoundedDrawable(bitmap, cornerRadius, margin));	}	public static class RoundedDrawable extends Drawable {		protected final float cornerRadius;		protected final int margin;		protected final RectF mRect = new RectF(),				mBitmapRect;		protected final BitmapShader bitmapShader;		protected final Paint paint;		public RoundedDrawable(Bitmap bitmap, int cornerRadius, int margin) {			this.cornerRadius = cornerRadius;			this.margin = margin;			bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);			mBitmapRect = new RectF (margin, margin, bitmap.getWidth() - margin, bitmap.getHeight() - margin);						paint = new Paint();			paint.setAntiAlias(true);			paint.setShader(bitmapShader);			paint.setFilterBitmap(true);			paint.setDither(true);		}		@Override		protected void onBoundsChange(Rect bounds) {			super.onBoundsChange(bounds);			mRect.set(margin, margin, bounds.width() - margin, bounds.height() - margin);						// Resize the original bitmap to fit the new bound			Matrix shaderMatrix = new Matrix();			shaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL);			bitmapShader.setLocalMatrix(shaderMatrix);					}		@Override		public void draw(Canvas canvas) {			canvas.drawRoundRect(mRect, cornerRadius, cornerRadius, paint);		}		@Override		public int getOpacity() {			return PixelFormat.TRANSLUCENT;		}		@Override		public void setAlpha(int alpha) {			paint.setAlpha(alpha);		}		@Override		public void setColorFilter(ColorFilter cf) {			paint.setColorFilter(cf);		}	}}

  

到此over了,流程通了。

----------------------------------------分割线----------------------------------------------- 总结: 1.保存图片:路径转流直接存文件,如果文件过大直接裁剪覆盖文件 2.加载图片:先看检查内存中是否缓存,未缓存拿本地缓存,根据配置裁剪和角度旋转 3.显示:根据配置定制不同的显示

转载于:https://www.cnblogs.com/wjw334/p/7280039.html

你可能感兴趣的文章
0809
查看>>
FineUIPro v5.2.0已发布(jQuery升级,自定义图标,日期控件)
查看>>
HTML页和ashx之间关系的一点小应用
查看>>
智能合约安全前传-基础知识入门
查看>>
Myeclipse反编译插件
查看>>
Dubbo和Zookerper的关系
查看>>
centos 5 系统安装MYSQL5.7
查看>>
docker数据卷(转)
查看>>
地图定位及大头针设置
查看>>
oracle常用小知识点
查看>>
CATransform3D参数的意义
查看>>
怎么自己在Objective-C中创建代理
查看>>
Under Armour Drive 4 Performance Reviews
查看>>
C#操作目录和文件
查看>>
警惕数组的浅拷贝
查看>>
百度地图 导航
查看>>
SQLServer 错误: 15404,无法获取有关 Windows NT 组
查看>>
html5全局属性
查看>>
【转】Android Hook框架Xposed详解
查看>>
Android 有用代码片段总结
查看>>