任务流图
任务流图中的每一步都有自己的接口(在图片底部)来负责这部分的任务。大部分接口(除了BitmapProcessor)都拥有默认的实现从左到右依次是BaseImageDowloader、UnlimitedDiscCache、BaseImageDecoder、LruMemoryCache、SimpleBitmapDisplayer
ImageDownloader
接口
public interface ImageDownloader {
InputStream getStream(String var1, Object var2) throws IOException;
//返回Schema
public static enum Schema{...}
}
基本实现
public class BaseImageDownloader implements ImageDownloader {
//...
//通过URL来从不同地方获取图片资源
public InputStream getStream(String imageUri, Object extra) throws IOException {
switch(...) {
case 1:
case 2:
return this.getStreamFromNetwork(imageUri, extra);
case 3:
return this.getStreamFromFile(imageUri, extra);
case 4:
return this.getStreamFromContent(imageUri, extra);
case 5:
return this.getStreamFromAssets(imageUri, extra);
case 6:
return this.getStreamFromDrawable(imageUri, extra);
case 7:
default:
return this.getStreamFromOtherSource(imageUri, extra);
}
}
//网络请求逻辑
protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
HttpURLConnection conn = this.createConnection(imageUri, extra);
for(int redirectCount = 0; conn.getResponseCode() / 100 == 3 && redirectCount < 5; ++redirectCount) {
conn = this.createConnection(conn.getHeaderField("Location"), extra);
}
InputStream imageStream;
try {
imageStream = conn.getInputStream();
} catch (IOException var7) {
IoUtils.readAndCloseStream(conn.getErrorStream());
throw var7;
}
if(!this.shouldBeProcessed(conn)) {
IoUtils.closeSilently(imageStream);
throw new IOException("Image request failed with response code " + conn.getResponseCode());
} else {
return new ContentLengthInputStream(new BufferedInputStream(imageStream, '耀'), conn.getContentLength());
}
}
//...
//创建Http请求对象
protected HttpURLConnection createConnection(String url, Object extra) throws IOException {
String encodedUrl = Uri.encode(url, "@#&=*+-_.,:!?()/~\'%");
HttpURLConnection conn = (HttpURLConnection)(new URL(encodedUrl)).openConnection();
conn.setConnectTimeout(this.connectTimeout);
conn.setReadTimeout(this.readTimeout);
return conn;
}
}
磁盘缓存
接口
public interface DiskCache {
File getDirectory();
File get(String var1);
boolean save(String var1, InputStream var2, CopyListener var3) throws IOException;
boolean save(String var1, Bitmap var2) throws IOException;
boolean remove(String var1);
void close();
void clear();
}
基本实现
- BaseDiskCache (LimitedAgeDiskCache、UnlimitedDiskCache)
- LruDiskCache
图片解码
接口
public interface ImageDecoder {
Bitmap decode(ImageDecodingInfo var1) throws IOException;
}
图片解码信息类
public class ImageDecodingInfo {
private final String imageKey;
private final String imageUri;
private final String originalImageUri;
private final ImageSize targetSize;
private final ImageScaleType imageScaleType;
private final ViewScaleType viewScaleType;
private final ImageDownloader downloader;
private final Object extraForDownloader;
private final boolean considerExifParams;
private final Options decodingOptions;
...
}
基本实现
public class BaseImageDecoder implements ImageDecoder {
...
public BaseImageDecoder(boolean loggingEnabled) {
this.loggingEnabled = loggingEnabled;
}
public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {
//通过decodingInfo里面的downloader获取图片流
InputStream imageStream = this.getImageStream(decodingInfo);
if(imageStream == null) {
L.e("No stream for image [%s]", new Object[]{decodingInfo.getImageKey()});
return null;
} else {
Bitmap decodedBitmap;
BaseImageDecoder.ImageFileInfo imageInfo;
try {
imageInfo = this.defineImageSizeAndRotation(imageStream, decodingInfo);
imageStream = this.resetStream(imageStream, decodingInfo);
Options decodingOptions = this.prepareDecodingOptions(imageInfo.imageSize, decodingInfo);
decodedBitmap = BitmapFactory.decodeStream(imageStream, (Rect)null, decodingOptions);
} finally {
IoUtils.closeSilently(imageStream);
}
if(decodedBitmap == null) {
L.e("Image can\'t be decoded [%s]", new Object[]{decodingInfo.getImageKey()});
} else {
decodedBitmap = this.considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation, imageInfo.exif.flipHorizontal);
}
return decodedBitmap;
}
}
....
}
内存缓存
接口
public interface MemoryCache {
boolean put(String var1, Bitmap var2);
Bitmap get(String var1);
Bitmap remove(String var1);
Collection<String> keys();
void clear();
}
基本实现
public class LruMemoryCache implements MemoryCache {
private final LinkedHashMap<String, Bitmap> map;
private final int maxSize;
private int size;
public LruMemoryCache(int maxSize) {
if(maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
} else {
this.maxSize = maxSize;
this.map = new LinkedHashMap(0, 0.75F, true);
}
}
public final Bitmap get(String key) {
if(key == null) {
throw new NullPointerException("key == null");
} else {
synchronized(this) {
return (Bitmap)this.map.get(key);
}
}
}
public final boolean put(String key, Bitmap value) {
if(key != null && value != null) {
synchronized(this) {
this.size += this.sizeOf(key, value);
Bitmap previous = (Bitmap)this.map.put(key, value);
if(previous != null) {
this.size -= this.sizeOf(key, previous);
}
}
this.trimToSize(this.maxSize);
return true;
} else {
throw new NullPointerException("key == null || value == null");
}
}
private void trimToSize(int maxSize) {
while(true) {
synchronized(this) {
if(this.size < 0 || this.map.isEmpty() && this.size != 0) {
throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!");
}
if(this.size > maxSize && !this.map.isEmpty()) {
Entry toEvict = (Entry)this.map.entrySet().iterator().next();
if(toEvict != null) {
String key = (String)toEvict.getKey();
Bitmap value = (Bitmap)toEvict.getValue();
this.map.remove(key);
this.size -= this.sizeOf(key, value);
continue;
}
}
return;
}
}
}
public final Bitmap remove(String key) {
if(key == null) {
throw new NullPointerException("key == null");
} else {
synchronized(this) {
Bitmap previous = (Bitmap)this.map.remove(key);
if(previous != null) {
this.size -= this.sizeOf(key, previous);
}
return previous;
}
}
}
public Collection<String> keys() {
synchronized(this) {
return new HashSet(this.map.keySet());
}
}
public void clear() {
this.trimToSize(-1);
}
private int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
public final synchronized String toString() {
return String.format("LruCache[maxSize=%d]", new Object[]{Integer.valueOf(this.maxSize)});
}
}
图片显示器
定义图片显示效果,形状或动画,很nice
接口
public interface BitmapDisplayer {
void display(Bitmap var1, ImageAware var2, LoadedFrom var3);
}
其中LoadedFrom代表图片加载的位置
public enum LoadedFrom {
NETWORK,
DISC_CACHE,
MEMORY_CACHE;
private LoadedFrom() {
}
}
基本实现
无特效
public final class SimpleBitmapDisplayer implements BitmapDisplayer {
public SimpleBitmapDisplayer() {
}
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
imageAware.setImageBitmap(bitmap);
}
}
圆形
好好学习,内部自定义了一个CircleDrawable
public class CircleBitmapDisplayer implements BitmapDisplayer {
protected final Integer strokeColor;
protected final float strokeWidth;
public CircleBitmapDisplayer() {
this((Integer)null);
}
public CircleBitmapDisplayer(Integer strokeColor) {
this(strokeColor, 0.0F);
}
public CircleBitmapDisplayer(Integer strokeColor, float strokeWidth) {
this.strokeColor = strokeColor;
this.strokeWidth = strokeWidth;
}
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
if(!(imageAware instanceof ImageViewAware)) {
throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
} else {
imageAware.setImageDrawable(new CircleBitmapDisplayer.CircleDrawable(bitmap, this.strokeColor, this.strokeWidth));
}
}
public static class CircleDrawable extends Drawable {
protected float radius;
protected final RectF mRect = new RectF();
protected final RectF mBitmapRect;
protected final BitmapShader bitmapShader;
protected final Paint paint;
protected final Paint strokePaint;
protected final float strokeWidth;
protected float strokeRadius;
public CircleDrawable(Bitmap bitmap, Integer strokeColor, float strokeWidth) {
this.radius = (float)(Math.min(bitmap.getWidth(), bitmap.getHeight()) / 2);
this.bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
this.mBitmapRect = new RectF(0.0F, 0.0F, (float)bitmap.getWidth(), (float)bitmap.getHeight());
this.paint = new Paint();
this.paint.setAntiAlias(true);
this.paint.setShader(this.bitmapShader);
this.paint.setFilterBitmap(true);
this.paint.setDither(true);
if(strokeColor == null) {
this.strokePaint = null;
} else {
this.strokePaint = new Paint();
this.strokePaint.setStyle(Style.STROKE);
this.strokePaint.setColor(strokeColor.intValue());
this.strokePaint.setStrokeWidth(strokeWidth);
this.strokePaint.setAntiAlias(true);
}
this.strokeWidth = strokeWidth;
this.strokeRadius = this.radius - strokeWidth / 2.0F;
}
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
this.mRect.set(0.0F, 0.0F, (float)bounds.width(), (float)bounds.height());
this.radius = (float)(Math.min(bounds.width(), bounds.height()) / 2);
this.strokeRadius = this.radius - this.strokeWidth / 2.0F;
Matrix shaderMatrix = new Matrix();
shaderMatrix.setRectToRect(this.mBitmapRect, this.mRect, ScaleToFit.FILL);
this.bitmapShader.setLocalMatrix(shaderMatrix);
}
public void draw(Canvas canvas) {
canvas.drawCircle(this.radius, this.radius, this.radius, this.paint);
if(this.strokePaint != null) {
canvas.drawCircle(this.radius, this.radius, this.strokeRadius, this.strokePaint);
}
}
public int getOpacity() {
return -3;
}
public void setAlpha(int alpha) {
this.paint.setAlpha(alpha);
}
public void setColorFilter(ColorFilter cf) {
this.paint.setColorFilter(cf);
}
}
}
渐入动画
public class FadeInBitmapDisplayer implements BitmapDisplayer {
private final int durationMillis;
private final boolean animateFromNetwork;
private final boolean animateFromDisk;
private final boolean animateFromMemory;
public FadeInBitmapDisplayer(int durationMillis) {
this(durationMillis, true, true, true);
}
public FadeInBitmapDisplayer(int durationMillis, boolean animateFromNetwork, boolean animateFromDisk, boolean animateFromMemory) {
this.durationMillis = durationMillis;
this.animateFromNetwork = animateFromNetwork;
this.animateFromDisk = animateFromDisk;
this.animateFromMemory = animateFromMemory;
}
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
imageAware.setImageBitmap(bitmap);
if(this.animateFromNetwork && loadedFrom == LoadedFrom.NETWORK || this.animateFromDisk && loadedFrom == LoadedFrom.DISC_CACHE || this.animateFromMemory && loadedFrom == LoadedFrom.MEMORY_CACHE) {
animate(imageAware.getWrappedView(), this.durationMillis);
}
}
public static void animate(View imageView, int durationMillis) {
if(imageView != null) {
AlphaAnimation fadeImage = new AlphaAnimation(0.0F, 1.0F);
fadeImage.setDuration((long)durationMillis);
fadeImage.setInterpolator(new DecelerateInterpolator());
imageView.startAnimation(fadeImage);
}
}
}
处理逻辑
入口
//所有的同步加载、异步加载、有回调的、没回调的都会最终调用这个方法
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListene
//在这检查是否init ImageLoderConfiguration
this.checkConfiguration();
//封装了ImageView的属性和操作
if(imageAware == null) {
throw new IllegalArgumentException("Wrong arguments were passed to displayImage() method (ImageView reference must not be null)");
} else {
if(listener == null) {
//默认为SimpleLoadingListener
listener = this.defaultListener;
}
if(options == null) {
//如果不设置图片显示属性,则使用默认值
options = this.configuration.defaultDisplayImageOptions;
}
//图片URL为空
if(TextUtils.isEmpty(uri)) {
this.engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
//是否设置图片URL为空时要设置的图片
if(options.shouldShowImageForEmptyUri()) {
imageAware.setImageDrawable(options.getImageForEmptyUri(this.configuration.resources));
} else {
imageAware.setImageDrawable((Drawable)null);
}
listener.onLoadingComplete(uri, imageAware.getWrappedView(), (Bitmap)null);
} else {
//图片URL不为空的情况
//没有特别设置图片显示尺寸的话,显示最大尺寸
if(targetSize == null) {
targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, this.configuration.getMaxImageSize());
}
//通过尺寸和URL生成内存缓存的键值
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
this.engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
//试图取出图片缓存
Bitmap bmp = this.configuration.memoryCache.get(memoryCacheKey);
ImageLoadingInfo imageLoadingInfo;
//有图片缓存&&bitmap没有被回收
if(bmp != null && !bmp.isRecycled()) {
L.d("Load image from memory cache [%s]", new Object[]{memoryCacheKey});
//是否要对图片进行特殊处理,比如加水印什么的(主要实现接口为BitmapProcessor)
if(options.shouldPostProcess()) {
//把图片加载的配置信息封装起来
imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri));
//Runnable类型的处理显示图片的任务,其实还是用调用LoadAndDisplayImageTask来执行的
ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, defineHandler(options));
if(options.isSyncLoading()) {
//同步加载
displayTask1.run();
} else {
//异步加载,提交到线程池
this.engine.submit(displayTask1);
}
} else {
//图片不需要特殊处理
//使用显示器显示,默认的实现(SimpleBitmapDisplayer)就是imageAware.setImageBirmap
options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
//回调图片加载完成
listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
}
} else {
if(options.shouldShowImageOnLoading()) {
//在加载阶段显示的图片
imageAware.setImageDrawable(options.getImageOnLoading(this.configuration.resources));
} else if(options.isResetViewBeforeLoading()) {
//加载前重置的图片
imageAware.setImageDrawable((Drawable)null);
}
imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri));
//新建加载和显示任务,也是Runnable类型的
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, defineHandler(options));
if(options.isSyncLoading()) {
displayTask.run();
} else {
this.engine.submit(displayTask);
}
}
}
}
任务类型
处理和显示
图片在内存已缓存,执行此任务
final class ProcessAndDisplayImageTask implements Runnable {
private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
private final ImageLoaderEngine engine;
private final Bitmap bitmap;
private final ImageLoadingInfo imageLoadingInfo;
private final Handler handler;
//...
public void run() {
L.d("PostProcess image before displaying [%s]", new Object[]{this.imageLoadingInfo.memoryCacheKey});
BitmapProcessor processor = this.imageLoadingInfo.options.getPostProcessor();
Bitmap processedBitmap = processor.process(this.bitmap);
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, this.imageLoadingInfo, this.engine, LoadedFrom.MEMORY_CACHE);
LoadAndDisplayImageTask.runTask(displayBitmapTask, this.imageLoadingInfo.options.isSyncLoading(), this.handler, this.engine);
}
}
加载和(处理和)显示
LoadAndDisplayImageTask,代码太多就不贴出来了,简单捋下逻辑:
1. 在run方法里面判断内存中是否有缓存,否则调用tryLoadBitmap
2. tryLoadBitmap首先判断磁盘是否有缓存,有则调用decodeImage返回Bitmap(经过以上分析,其实是调用了ImageDecoder,而在decoder里面又会调用ImageDowloader的getStream获得要解码的图片流)
3. 如果磁盘没有缓存,则调用网络获取图片流
执行引擎
从磁盘、网络加载图片等都是耗时操作,需要使用线程池执行操作。ImageLoaderEngine,顾名思义为UIL的整个动力源,没错它里面有三个线程池。
class ImageLoaderEngine {
//分发任务,在run里面分发不同任务到下面两个线程池
private Executor taskExecutor;
//执行ProcessAndDisplayImageTask任务,从缓存(磁盘、内存)里面读
private Executor taskExecutorForCachedImages;
//执行LoadAndDisplayImageTask,从网络读
private Executor taskDistributor;
//...
void submit(final LoadAndDisplayImageTask task) {
this.taskDistributor.execute(new Runnable() {
public void run() {
File image = ImageLoaderEngine.this.configuration.diskCache.get(task.getLoadingUri());
boolean isImageCachedOnDisk = image != null && image.exists();
ImageLoaderEngine.this.initExecutorsIfNeed();
if(isImageCachedOnDisk) {
ImageLoaderEngine.this.taskExecutorForCachedImages.execute(task);
} else {
ImageLoaderEngine.this.taskExecutor.execute(task);
}
}
});
}
void submit(ProcessAndDisplayImageTask task) {
this.initExecutorsIfNeed();
this.taskExecutorForCachedImages.execute(task);
}
}
//...
相关阅读:UIL源码解析