github链接
实现图片加载功能的第三方图片加载库,需要添加依赖
implementation 'com.squareup.picasso:picasso:2.71828'
Picasso.with(context)//拿到Picasso对象
.load(url)//load一个url
.resize(width, height)//重新裁剪大小
.memoryPolicy(MemoryPolicy.NO_STORE)//内存缓存策略(不存入内存)
.networkPolicy(NetworkPolicy.OFFLINE)//磁盘缓存策略(不通过网络请求)
.into(imageView);//显示到ImageView中
public static Picasso with(Content context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder().build();
}
}
}
return singleton;
}
Picasso使用的是单例模式,并且使用Builder模式创建了一个Picasso的实例。有了实例之后,直接调用它的load函数,Picasso重载了几个不同参数的load函数,用以从不同的地点来加载图片
public RequestCreator load(Uri uri) {
...
}
public RequestCreator load(String path) {
...
}
public RequestCreator load(int resourceId) {
...
}
通过load函数,我们最终得到了一个RequestCreator对象,RequestCreator对象提供了各种占位图的设置方法以及缓存策略的方法。其内部封装了一个Request.Builder对象,用于设置图片相关参数,也是通过builder模式创建;如加载的uri、resize的大小、裁剪方式等
最后我们调用了into函数,将加载到的图片赋给一个ImageView控件:
public void into(Target target) {
long started = System.nanoTime();
checkMain();
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
if (deferred) {
throw new IllegalStateException("Fit cannot be used with a Target.");
}
if (!data.hasImage()) {
picasso.cancelRequest(target);
target.onPrepareLoad(setPlaceholder ? getPlaceholderDrawable() : null);
return;
}
Request request = createRequest(started);
String requestKey = createKey(request);
if (shouldReadFromMemoryCache(memoryPolicy)) {
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
picasso.cancelRequest(target);
target.onBitmapLoaded(bitmap, MEMORY);
return;
}
}
target.onPrepareLoad(setPlaceholder ? getPlaceholderDrawable() : null);
Action action =
new TargetAction(picasso, target, request, memoryPolicy, networkPolicy, errorDrawable,
requestKey, tag, errorResId);
picasso.enqueueAndSubmit(action);
}
至此,Picasso在主线程中的工作就结束了:首先通过Picasso创建了一个RequestCreator对象,通过这个对象我们可以针对不同的场景来设置一些属性,之后创建出Request对象,最后通过Action来确定异步请求并对请求结果做处理。
public Picasso build() {
Context context = this.context;
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
if (cache == null) {
cache = new LruCache(context);
}
if (service == null) {
service = new PicassoExecutorService();
}
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}
Stats stats = new Stats(cache);
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
RequestCreator
在into方法的最后会创建一个Action实例,然后调用Picasso
的enqueueAndSubmit
方法,而最终是调用了Dispatcher
的dispatchSubmit
方法。在Dispatcher
内部,Dispatcher
定义了DispatcherThread
和DispatcherHandler
两个内部类,并在Dispatcher
的构造函数中对他们经行了实例化,所有的调度也都是通过handler
异步的执行的,handler
最终调用performSubmit
方法来触发一个图片的加载,在这个方法中主要是获取BitmapHunter
实例,由这个实例来执行实际的下载操作。BitmapHunter
本身是Runnable
的一个实现,而这个实例最终是交由Picasso
线程池进行运行的。加载完成或失败时,还是通过Dispatcher调度器实现UI更新操作,最终会执行performComplete
方法。这个方法会自动处理图片的缓存问题
dispatcher.dispatchComplete(this)
void dispatchComplete(BitmapHunter hunter) {
handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
}
public void handleMessage(final Message msg) {
switch (msg.what) {
...
case HUNTER_COMPLETE: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performComplete(hunter);
break;
}
...
}
}
void performComplete(BitmapHunter hunter) {
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
cache.set(hunter.getKey(), hunter.getResult());
}
hunterMap.remove(hunter.getKey());
batch(hunter);
if (hunter.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
}
}
这个时候,Picasso并不是立即将图片显示出来,而是用到了一个批处理,其实就是把操作先暂存在一个list中,等空闲的时候再拿出来处理,这样做得好处也是尽量减少主线程的执行时间,一方面防止ANR,另一方面快速返回,响应页面的其他渲染操作,防止卡顿用户界面。
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
public void handleMessage(final Message msg) {
switch (msg.what) {
...
case HUNTER_DELAY_NEXT_BATCH: {
dispatcher.performBatchComplete();
break;
}
...
}
}
最后还是调用了Dispatcher的方法来处理,但由于这个处理并非是在主线程,因此还需要通过一个主线程的Handler来处理这个请求
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
这个mainThreadHandler是在Dispatcher实例化时由外部传递进来的,Picasso在通过Builder创建时会对Dispatcher进行实例化,在那个地方将主线程的handler传了进来,回到Picasso这个类,看到其有一个静态成员变量HANDLER。
static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case HUNTER_BATCH_COMPLETE: {
@SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = batch.size(); i < n; i++) {
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter);
}
break;
}
...
}
}
};
Picasso中一个Action提供了请求前后的衔接工作,对于现在的情况,Picasso使用了ImageViewAction
来进行处理,也就是在ImageViewAction
中的complete方法完成了最后的图片渲染工作。
public void complete(Bitmap result, Picasso.LoadedFrom from) {
if (result == null) {
throw new AssertionError(
String.format("Attempted to complete action with no result!\n%s", this));
}
ImageView target = this.target.get();
if (target == null) {
return;
}
Context context = picasso.context;
boolean indicatorsEnabled = picasso.indicatorsEnabled;
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
if (callback != null) {
callback.onSuccess();
}
}