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
共同學(xué)習(xí),寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章