最近一直在研究Xutil代码的解析,发现代码写的不错,无论是从代码的质量上还是设计上堪称经典,所以写一下它的解析,看一下它的设计理念,对咱们日常的开发起到很大的作用。
Xutil系列分为几个系列,先写第一个BitmapUtil的解析,它是异步加载图片,采用线程池下载,图片显示和缓存一些列的功能集合。
1:使用:
bit = new BitmapUtils(activity);
bit.display(holder.imageView, list.get(position).getPic(), new DefaultBitmapLoadCallBack() {
@Override
public void onLoadCompleted(ImageView container, String uri, Bitmap bitmap, BitmapDisplayConfig config, BitmapLoadFrom from) {
super.onLoadCompleted(container, uri, zoomBitmap(bitmap, (int) (180 * b), (int) (100 * b)), config, from);
holder.imageView.setImageBitmap(zoomBitmap(bitmap, (int) (180 * b), (int) (100 * b)));
}
@Override
public void onLoadFailed(ImageView container, String uri, Drawable drawable) {
super.onLoadFailed(container, uri, drawable);
Bitmap bitmap = BitmapFactory.decodeResource(activity.getResources(), R.drawable.default10);
container.setImageBitmap(zoomBitmap(bitmap, (int) (180 * b), (int) (100 * b)));
}
});
使用起来非常简单,传递控件,url地址,图片加载成功和失败的回调。当然你也可以不写图片加载之后的回调。
2:我们具体研究的是代码设计。
看这个实际上看不出来啥大笑,主要是能明白这个类主要的功能逻辑和代码结构。上面的调用其实很简单,走一遍他的加载流程。
public interface TaskHandler {
boolean supportPause();
boolean supportResume();
boolean supportCancel();
void pause();
void resume();
void cancel();
boolean isPaused();
boolean isCancelled();
}
这个父接口,起到控制作用,对整个加载图片的流程进行了控制和梳理。
public class BitmapUtils implements TaskHandler {
private boolean pauseTask;
private boolean cancelAllTask;
private final Object pauseTaskLock;
private Context context;
private BitmapGlobalConfig globalConfig;
private BitmapDisplayConfig defaultDisplayConfig;
代码里面有任务锁,因为是多任务,在Adapter中的getview时,可能一下子要加载20多张图片。那么它是如何实现多线程下载的呢?偷笑
public BitmapUtils(Context context, String diskCachePath, int memoryCacheSize, int diskCacheSize) {
this(context, diskCachePath);
this.globalConfig.setMemoryCacheSize(memoryCacheSize);
this.globalConfig.setDiskCacheSize(diskCacheSize);
}
先看一下他的构造函数,里面涉及到文件缓存,内存缓存,可以看出,
globalConfig是他的配置属性。关于配置的一些属性交给他来进行处理、
BitmapDisplayConfig 是负责展示图片。
后续这两块我们还会进行详解。
public void display(T container, String uri, BitmapDisplayConfig displayConfig, BitmapLoadCallBack callBack) {
if(container != null) {
if(callBack == null) { // 图片加载之后的回调
callBack = new DefaultBitmapLoadCallBack();
}
if(displayConfig == null || displayConfig == this.defaultDisplayConfig) {
// 显示配置,稍后我们会详细介绍这块。
displayConfig = this.defaultDisplayConfig.cloneNew();
}
BitmapSize size = displayConfig.getBitmapMaxSize();
displayConfig.setBitmapMaxSize(BitmapCommonUtils.optimizeMaxSizeByView(container, size.getWidth(), size.getHeight()));
container.clearAnimation();
if(TextUtils.isEmpty(uri)) {
((BitmapLoadCallBack)callBack).onLoadFailed(container, uri, displayConfig.getLoadFailedDrawable());
} else {// 注意加载逻辑,是先从内存缓存中取
((BitmapLoadCallBack)callBack).onPreLoad(container, uri, displayConfig);
Bitmap bitmap = this.globalConfig.getBitmapCache().getBitmapFromMemCache(uri, displayConfig);
if(bitmap != null) {
((BitmapLoadCallBack)callBack).onLoadStarted(container, uri, displayConfig);
((BitmapLoadCallBack)callBack).onLoadCompleted(container, uri, bitmap, displayConfig, BitmapLoadFrom.MEMORY_CACHE);
} else if(!bitmapLoadTaskExist(container, uri, (BitmapLoadCallBack)callBack)) {// 没有的情况下才联网获取,没有的话开启异步任务喽,后续我们会进一步的研究这个异步任务类
BitmapUtils.BitmapLoadTask loadTask = new BitmapUtils.BitmapLoadTask(container, uri, displayConfig, (BitmapLoadCallBack)callBack);
PriorityExecutor executor = this.globalConfig.getBitmapLoadExecutor();
File diskCacheFile = this.getBitmapFileFromDiskCache(uri);
boolean diskCacheExist = diskCacheFile != null && diskCacheFile.exists();
if(diskCacheExist && executor.isBusy()) {
executor = this.globalConfig.getDiskCacheExecutor();
}
Drawable loadingDrawable = displayConfig.getLoadingDrawable();
((BitmapLoadCallBack)callBack).setDrawable(container, new AsyncDrawable(loadingDrawable, loadTask));
loadTask.setPriority(displayConfig.getPriority());
loadTask.executeOnExecutor(executor, new Object[0]);
}
}
}
}
这块代码就是具体的展示,采用了JAVA泛型,有兴趣的可以多了解一下,主要是为了适配各种View,上面作了部分注解,看下面的代码,在一个不知道有多少下载图片的任务的下载阶段,我们如何来进行多任务的下载和内存优化,唯一的答案就是利用好线程池。
private static boolean bitmapLoadTaskExist(T container, String uri, BitmapLoadCallBack callBack) {
BitmapUtils.BitmapLoadTask oldLoadTask = getBitmapTaskFromContainer(container, callBack);
if(oldLoadTask != null) {
String oldUrl = oldLoadTask.uri;
if(!TextUtils.isEmpty(oldUrl) && oldUrl.equals(uri)) {
return true;
}
oldLoadTask.cancel(true);
}
return false;
}
判断有没有存在这个线程任务。
下面是完整的内部任务类
public class BitmapLoadTask extends PriorityAsyncTask