image
介绍
ImageLoader是android使用中出现比较早,使用最多的一个开源图片加载库了,随着glide,picasso等图片加载的库出现,ImageLoader使用变得越来越少。
特点
支持网络,本地加载图片
多线程下载图片
支持图片下载进度的监听
支持Listview滚动时暂停下载
支持内存和磁盘的图片缓存
支持自定义配置选项(线程执行器,下载器,编码器,缓存,显示图片等)
本文也是采用调用流程为线的方式来分析源码,下面我们先来看下ImageLoader整体流程图和核心类的名称和作用。
预习
流程图:
img_01.png
重要的类:
ImageLoaderConfiguration
配置信息类,使用builder设计模式
ImageLoaderEngine
图片加载引擎,主要负责将LoadAndDisplayImageTaskProcessAndDisplayImageTask任务分发给线程池去执行 Executor taskExecutor; // 用于从源获取图片的线程池Executor taskExecutorForCachedImages; // 从缓存池中获取图片的线程池Executor taskDistributor; // 分发任务的线程池,把任务分发到上面两个线程池中
ImageAware
图片显示的对象,一般为ImageView的封装
ImageDownloader
图片下载器,主要是从本地,网络等获取图片流
MemoryCache
内存缓存,使用Lru算法实现对内存缓存的管理,当图片占用内存或者图片数量大于设置的阈值,回收最老的,最少使用的。
DiskCache
本地缓存,可以自定义配置,实现对本地缓存的控制
ImageDecoder
图片解码器,将输入流转化成Bitmap
BitmapProcessor
图片处理器,图片的预处理和后期处理都使用这个类,图片的处理包括图片宽高等等
BitmapDisplayer
bitmap显示器,负责将处理过后的bitmap显示
LoadAndDisplayImageTask
加载并显示图片任务
ProcessAndDisplayImageTask
处理并显示图片任务
DisplayBitmapTask
显示图片任务
源码解析
本文不打算对ImageLoaderConfiguration配置类进行讲解,其实就是进行配置,还有使用了Builder设置模式,有兴趣的可以去看源码。
1.ImageLoader.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;
} //图片宽高为空,设置为imageview宽高
if (targetSize == null) {
targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
} //通过uri+targetsize生成缓存 key
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize); //将imageAware和缓存key关联
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); //有缓存且可用
//是否支持后期处理bitmap
if (options.shouldPostProcess()) { //封装ImageLoadingInfo,封装处理显示任务
//defineHandler()如果同步,返回null
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()) { //直接执行run(当前线程)
displayTask.run();
} else { //将任务交给taskExecutorForCachedImages处理缓存的线程池来执行
engine.submit(displayTask);
}
} else { //不支持后期处理bitmap
//显示bitmap,加载完毕
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,封装加载显示任务
//defineHandler()如果同步,返回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()) { //同步执行run
displayTask.run();
} else { //任务交给taskDistributor进行派发(根据是否有本地缓存)
engine.submit(displayTask);
}
}
}displayImage方法中主要就是:
生成 cacheKey,并和当前ImageAware保存到map中
作用是,在线程中判断当前ImageAware是否被重用了
有缓存,异步,生成ProcessAndDisplayImageTask,且交给缓存线程池taskExecutorForCachedImages
没有缓存,异步,生成LoadAndDisplayImageTask,交给分发线程池taskDistributor来根据是否有本地缓存来分发
关于同步我们就不讲解了,我们下面从两个方面讲解:有内存缓存异步,没有内存缓存异步。
2.有内存缓存异步
2.1 ProcessAndDisplayImageTask.run()
public void run() {
//后期处理图片
BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor();
Bitmap processedBitmap = processor.process(bitmap); //创建DisplayBitmapTask(显示任务)
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine,
LoadedFrom.MEMORY_CACHE); //执行显示任务
LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine);
}对来自内存缓存中的图片进行后期处理,创建DisplayBitmapTask,交给LoadAndDisplayImageTask的runTask方法去执行。
2.2 LoadAndDisplayImageTask.runTask
static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) { if (sync) { //同步执行,当前线程显示
r.run();
} else if (handler == null) { //异步执行,且在执行线程中显示
engine.fireCallback(r);
} else { //handler执行(主线程显示)
handler.post(r);
}
}runTask方法就是判断显示任务在那个线程中执行。
2.3 DisplayBitmapTask.run
public void run() { //imageAware被回收
if (imageAware.isCollected()) {
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
} else if (isViewWasReused()) { //imageAware被重用
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
} else { //显示图片
displayer.display(bitmap, imageAware, loadedFrom); //移除cacheKeysForImageAwares中当前iamgeAware元素
engine.cancelDisplayTaskFor(imageAware); //调用加载完成监听
listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
}
}如果ImageAware被回收或者被重用,那么直接回调onLoadingCancelled,负责显示图片,移除cacheKeysForImageAwares中当前ImageAware的数据,回调onLoadingComplete。
2.4 isViewWasReused()
private boolean isViewWasReused() { //获取ImageAware最新的cacheKey
String currentCacheKey = engine.getLoadingUriForView(imageAware); //如果当前任务的cacheKey和最新的cacheKey不一致,说明ImageAware被重用了。
return !memoryCacheKey.equals(currentCacheKey);
}如果当前任务的cacheKey和最新的cacheKey不一致,说明ImageAware被重用了。
有内存缓存的情况已经分析结束。
3.没有内存缓存异步
3.1 ImageLoaderEngine.submit()
void submit(final LoadAndDisplayImageTask task) {
taskDistributor.execute(new Runnable() { @Override
public void run() { //获取本地缓存
File image = configuration.diskCache.get(task.getLoadingUri()); boolean isImageCachedOnDisk = image != null && image.exists();
initExecutorsIfNeed(); //是否有本地缓存
if (isImageCachedOnDisk) { //如果有本地缓存,交给处理缓存线程池
taskExecutorForCachedImages.execute(task);
} else { //如果没有本地缓存,交给taskExecutor
taskExecutor.execute(task);
}
}
});
}分发线程池taskDistributor根据是否有本地缓存来进行处理:
有本地缓存,将任务交给缓存线程池
没有本地缓存,将任务交给taskExecutor
3.2 LoadAndDisplayImageTask.run()
public void run() { //如果线程被打断,进入休眠
//如果线程没有被打断或者被唤醒,且imageAware被GC回收,或者imageAware被重用了,那么返回true
if (waitIfPaused()) return; //延时加载且imageAware被GC回收,或者imageAware被重用了,那么返回true
if (delayIfNeed()) return; //根据当前uri,获取锁
ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
//加锁
loadFromUriLock.lock();
Bitmap bmp; try { //如果ImageAware被回收了或者被重用了,直接抛出任务取消异常TaskCancelledException
checkTaskNotActual(); //获取内存缓存(有可能图片被其他线程加载过)
bmp = configuration.memoryCache.get(memoryCacheKey); if (bmp == null || bmp.isRecycled()) { //没有内存缓存
//尝试从本地缓存或者本地或者网络等等源加载图片
bmp = tryLoadBitmap(); //失败的onLoadingFailed监听已经被回调,这边直接返回
if (bmp == null) return; // listener callback already was fired
//处理图片前,检查ImageAware被回收了,或者被重用了,或者线程被打断了
checkTaskNotActual();
checkTaskInterrupted(); //内存缓存前的预处理
if (options.shouldPreProcess()) {
bmp = options.getPreProcessor().process(bmp);
} //添加到内存缓存中
if (bmp != null && options.isCacheInMemory()) {
configuration.memoryCache.put(memoryCacheKey, bmp);
}
} else { //有内存缓存
loadedFrom = LoadedFrom.MEMORY_CACHE;
} //内存缓存后的处理
if (bmp != null && options.shouldPostProcess()) {
bmp = options.getPostProcessor().process(bmp);
} ////处理图片后,检查ImageAware被回收了,或者被重用了,或者线程被打断了
checkTaskNotActual();
checkTaskInterrupted();
} catch (TaskCancelledException e) { //异步且线程不是被打断的,执行onLoadingCancelled
fireCancelEvent(); return;
} finally { //释放锁
loadFromUriLock.unlock();
} //创建DisplayBitmapTask任务
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom); //执行
runTask(displayBitmapTask, syncLoading, handler, engine);
}代码注释很清晰的,来个简单的流程总结:
1.imageAware被GC回收,或者imageAware被重用了,直接返回true
2.加锁
3.获取内存缓存
有缓存:
4.后期的处理
5.释放锁
6.创建DisplayBitmapTask任务,并执行
没有缓存
4.从本地缓存中获取,如果没有从网络,sd卡等中获取图片
5.预处理
6.添加到内存缓存中
7.后期的处理
8.释放锁
9.创建DisplayBitmapTask任务,并执行
下面分析核心的方法:
3.2.1 waitIfPaused() && delayIfNeed()
3.2.1.1 waitIfPaused()
/**
* 如果线程被打断(比如:滚动listview),线程进入wait,并释放锁
* 如果线程没有被打断或者被唤醒,你们返回isTaskNotActual()
* isTaskNotActual --- >如果imageAware被GC回收,或者imageAware被重用了,那么返回true
*
* @return
*/
private boolean waitIfPaused() { //获取是否暂停状态
AtomicBoolean pause = engine.getPause(); if (pause.get()) { synchronized (engine.getPauseLock()) { if (pause.get()) {
L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey); try { //休眠,等待被唤醒
engine.getPauseLock().wait();
} catch (InterruptedException e) {
L.e(LOG_TASK_INTERRUPTED, memoryCacheKey); return true;
}
L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
}
}
} return isTaskNotActual();
}如果当前的状态为暂停(pasue = true),那么进入休眠,等待被唤醒
如果没有暂停或者被唤醒,那么返回isTaskNotActual()结果
上面代码有两个点需要注意:1.何时暂停,何时被唤醒。 2.isTaskNotActual()执行
1.何时暂停,何时被唤醒
我们都知道当ListView滑动的时候,ImageLoader会暂停图片加载,停止滑动时,继续加载图片,这就是暂停和唤醒的时机:
PauseOnScrollListener.onScrollStateChanged()
public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case OnScrollListener.SCROLL_STATE_IDLE: //恢复加载(唤醒)
imageLoader.resume(); break; case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: if (pauseOnScroll) { //滑动,暂停加载(休眠)
imageLoader.pause();
} break; case OnScrollListener.SCROLL_STATE_FLING: if (pauseOnFling) { //滚动,暂停加载(休眠)
imageLoader.pause();
} break;
}
...
}ImageLoaderEngine.java
private final AtomicBoolean paused = new AtomicBoolean(false);void pause() {
paused.set(true);
}void resume() {
paused.set(false);
synchronized (pauseLock) { //唤醒所有任务
pauseLock.notifyAll();
}
}2.isTaskNotActual()
/**
* 如果imageAware被GC回收,或者imageAware被重用了,那么返回true
* @return
*/
private boolean isTaskNotActual() { return isViewCollected() || isViewReused();
} /**
* imageAware是否被GC回收
*
* @return 回收 --true
*/
private boolean isViewCollected() { if (imageAware.isCollected()) { return true;
} return false;
}
/**
* ImageAware是否被重用,来显示其他图片
* 以开始执行时,存放在cacheKeysForImageAwares中的cacheKey为最新的key
* 如果当前的cachekey不相同,则停止舍弃当前的加载任务
*
* @return true--重用
*/
private boolean isViewReused() { //获取最新的cachekey
String currentCacheKey = engine.getLoadingUriForView(imageAware); //如果和当前cachekey不一致,说明imageAware被重用了
boolean imageAwareWasReused = !memoryCacheKey.equals(currentCacheKey); if (imageAwareWasReused) { return true;
} return false;
}waitIfPaused()总结:
如果暂停加载,进入休眠等待被唤醒
如果没有暂停,或者被唤醒,则ImageAware被重用或者imageAware被GC回收,返回true,直接结束
3.2.1.2 delayIfNeed()
private boolean delayIfNeed() { //延时加载
if (options.shouldDelayBeforeLoading()) { try { //睡眠
Thread.sleep(options.getDelayBeforeLoading());
} catch (InterruptedException e) { return true;
} return isTaskNotActual();
} return false;
}delayIfNeed总结:
如果没有延时加载,返回false
如果延时加载,进入睡眠
线程被打断结束睡眠 ,返回true
正常结束睡眠,则ImageAware被重用或者imageAware被GC回收,返回true,直接结束
3.2.2 checkTaskNotActual() && checkTaskInterrupted()
//如果ImageAware被GC回收,抛出TaskCancelledExceptionprivate void checkViewCollected() throws TaskCancelledException { if (isViewCollected()) { throw new TaskCancelledException();
}
}//如果ImageAware被重用,抛出TaskCancelledExceptionprivate void checkViewReused() throws TaskCancelledException { if (isViewReused()) { throw new TaskCancelledException();
}
}checkTaskNotActual总结:
如果ImageAware被GC回收或者ImageAware被重用,抛出TaskCancelledException异常
抛出异常的处理将在后面进行讲解
//如果线程被打断,抛出TaskCancelledException
private void checkTaskInterrupted() throws TaskCancelledException { if (isTaskInterrupted()) { throw new TaskCancelledException();
}
} //如果线程被打断,返回true
private boolean isTaskInterrupted() { if (Thread.interrupted()) { return true;
} return false;
}checkTaskInterrupted总结:
如果线程被打断,抛出TaskCancelledException异常
抛出异常的处理将在后面进行讲解
3.2.3 TaskCancelledException 异常处理
private void fireCancelEvent() { //同步或者线程被打断,直接返回
if (syncLoading || isTaskInterrupted()) return;
Runnable r = new Runnable() { @Override
public void run() {
listener.onLoadingCancelled(uri, imageAware.getWrappedView());
}
}; //执行onLoadingCancelled
runTask(r, false, handler, engine);
}如果是同步或者线程被打断,直接返回,否则在指定线程中回调onLoadingCancelled
3.2.4 tryLoadBitmap()
获取图片的核心方法:
private Bitmap tryLoadBitmap() throws TaskCancelledException {
Bitmap bitmap = null; try { //获取本地缓存
File imageFile = configuration.diskCache.get(uri); if (imageFile != null && imageFile.exists() && imageFile.length() > 0) { //有本地缓存
loadedFrom = LoadedFrom.DISC_CACHE; //检查
checkTaskNotActual(); //解码(file-->inputstream--->bitmap)
bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
} //没有本地缓存
if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
loadedFrom = LoadedFrom.NETWORK;
String imageUriForDecoding = uri; //如果开启本地缓存则调用tryCacheImageOnDisk从network,asset,content,file,drawable中获取图片,且缓存到本地缓存中
if (options.isCacheOnDisk() && tryCacheImageOnDisk()) { //获取到下载的文件
imageFile = configuration.diskCache.get(uri); if (imageFile != null) {
imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());
}
} //检查
checkTaskNotActual(); //如果上面已经获取了图片,则这里的uri为本地uri
//如果没有,下面将从network,asset,content,file,drawable中获取图片
//内存保存的是根据ImageView大小、scaletype、方向处理过得图片
bitmap = decodeImage(imageUriForDecoding); if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) { //如果是异步且线程没有被打断,imageaware没有被回收和重用
// 那么获取图片失败,显示失败后的图片,并且调用onLoadingFailed
fireFailEvent(FailType.DECODING_ERROR, null);
}
}
} catch (IllegalStateException e) { //如果是异步且线程没有被打断,imageaware没有被回收和重用
// 那么获取图片失败,显示失败后的图片,并且调用onLoadingFailed
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;
}tryLoadBitmap流程:
1.获取本地缓存
2.如果有本地缓存,直接解码取出
3.如果开启了本地缓存,则下载图片,并将图片保存到本地缓存中
4.解码图片,返回
分析几个核心方法:
1.tryCacheImageOnDisk
/**
* 从network,asset,content,file,drawable中获取图片,且缓存到本地缓存中
* @return 获取图片,保存本地成功返回true
* @throws TaskCancelledException
*/
private boolean tryCacheImageOnDisk() throws TaskCancelledException { boolean loaded; try { //获取图片,并保存本地缓存
loaded = downloadImage(); if (loaded) { //将原图转化为设定的最大本地缓存图片大小(默认为0,所以不要改边)
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) {
loaded = false;
} return loaded;
}
private boolean downloadImage() throws IOException { //下载图片
InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader()); if (is == null) { return false;
} else { try { //保存到磁盘中
return configuration.diskCache.save(uri, is, this);
} finally {
IoUtils.closeSilently(is);
}
}
}BaseImageDownloader.getStream()
public InputStream getStream(String imageUri, Object extra) throws IOException { switch (Scheme.ofUri(imageUri)) { case HTTP: case HTTPS: //网络图片通过HttpURLConnection实现的
return getStreamFromNetwork(imageUri, extra); case FILE: return getStreamFromFile(imageUri, extra); case CONTENT: return getStreamFromContent(imageUri, extra); case ASSETS: return getStreamFromAssets(imageUri, extra); case DRAWABLE: return getStreamFromDrawable(imageUri, extra); case UNKNOWN: default: return getStreamFromOtherSource(imageUri, extra);
}
}getStream总结:
根据uri协议的不同,从不同的源加载图片
tryCacheImageOnDisk总结:
获取图片,保存到本地缓存
2.decodeImage
private Bitmap decodeImage(String imageUri) throws IOException {
ViewScaleType viewScaleType = imageAware.getScaleType(); //targetSize -- imageview大小或者内存缓存的最大size
ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,
getDownloader(), options); return decoder.decode(decodingInfo);
}BaseImageDecoder.decode()
public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {
Bitmap decodedBitmap;
ImageFileInfo imageInfo; //获取图片流
InputStream imageStream = getImageStream(decodingInfo); if (imageStream == null) { return null;
} try { //确定图片尺寸 和 旋转角度 ,生成ImageFileInfo
imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo); //重置流游标
imageStream = resetStream(imageStream, decodingInfo); //BitmapFactory.options
//准备decode的opions
//decodingOptions.inSampleSize = scale; 对bitmap进行压缩
Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo);
decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions);
} finally {
IoUtils.closeSilently(imageStream);
} if (decodedBitmap == null) {
} else { //对Bitmap进行缩放,翻转和旋转等操作
decodedBitmap = considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation,
imageInfo.exif.flipHorizontal);
} return decodedBitmap;
}getImageStream():
//调用BaseImageDownloader.getStream()
protected InputStream getImageStream(ImageDecodingInfo decodingInfo) throws IOException { return decodingInfo.getDownloader().getStream(decodingInfo.getImageUri(), decodingInfo.getExtraForDownloader());
}defineImageSizeAndRotation()
//获取图片的size 和 旋转角度(EXIF信息)
protected ImageFileInfo defineImageSizeAndRotation(InputStream imageStream, ImageDecodingInfo decodingInfo) { //BitmapFactory.options
Options options = new Options(); //不加载bitmap数据,只返回bitmap信息
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(imageStream, null, options); //EXIF信息,是可交换图像文件的缩写,是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。
ExifInfo exif;
String imageUri = decodingInfo.getImageUri(); //考虑 exif 且 文件能够定义 exif(JPEG文件)
//附加:EXIF可以附加于JPEG、TIFF、RIFF等文件之中
if (decodingInfo.shouldConsiderExifParams() && canDefineExifParams(imageUri, options.outMimeType)) { //根据文件exif信息,获取旋转角度,保存到ExifInfo中
exif = defineExifOrientation(imageUri);
} else { //创建对象(角度为0,即不旋转)
exif = new ExifInfo();
} //将 图片的宽高,旋转角度 ,exif 构建ImageFileInfo对象
return new ImageFileInfo(new ImageSize(options.outWidth, options.outHeight, exif.rotation), exif);
}resetStream()
//重置游标
protected InputStream resetStream(InputStream imageStream, ImageDecodingInfo decodingInfo) throws IOException { if (imageStream.markSupported()) { try { //重置游标
imageStream.reset(); return imageStream;
} catch (IOException ignored) {
}
}
IoUtils.closeSilently(imageStream); return getImageStream(decodingInfo);
}prepareDecodingOptions():
准备decode的opions,根据ImageScaleType,imagesize计算decodingOptions.inSampleSize 的值。
considerExactScaleAndOrientatiton()
//对Bitmap进行缩放,翻转和旋转等操作
protected Bitmap considerExactScaleAndOrientatiton(Bitmap subsampledBitmap, ImageDecodingInfo decodingInfo, int rotation, boolean flipHorizontal) {
Matrix m = new Matrix(); // Scale to exact size if need
//缩放到指定size
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
//翻转bitmap
if (flipHorizontal) {
m.postScale(-1, 1); if (loggingEnabled) L.d(LOG_FLIP_IMAGE, decodingInfo.getImageKey());
} // Rotate bitmap if need
//旋转bitmap
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;
}decodeImage总结:
获取bitmap,根据ImageView大小、scaletype、方向,旋转角度处理图片,最后返回
3.fireFailEvent
/**
* 如果是异步且线程没有被打断,imageaware没有被回收和重用
那么获取图片失败,显示失败后的图片,并且调用onLoadingFailed
* @param failType
* @param failCause
*/
private void fireFailEvent(final FailType failType, final Throwable failCause) { if (syncLoading || isTaskInterrupted() || isTaskNotActual()) return;
Runnable r = new Runnable() { @Override
public void run() { //设置失败后的图片
if (options.shouldShowImageOnFail()) {
imageAware.setImageDrawable(options.getImageOnFail(configuration.resources));
}
listener.onLoadingFailed(uri, imageAware.getWrappedView(), new FailReason(failType, failCause));
}
};
runTask(r, false, handler, engine);
}
作者:爱听音乐的小石头
链接:https://www.jianshu.com/p/88d2a255e872
共同学习,写下你的评论
评论加载中...
作者其他优质文章

