源码基于Picasso 2.5.2。
一,概括
Picasso.with(context).load(“url”).into(imageView);
从Picasso最简单的调用来一观Picasso的整体流程。
给个图:
with方法:通过Builder构建了一个Picasso单例。
load方法:创建一个RequestCreator类,RequestCreator在构造方法创建了Request.Builder,可以通过Builder的build方法创建一个Request。
into方法:通过Request构建了ImageViewAction,然后由Picasso添加到请求队列里。Picasso提交给Dispatcher ,Dispatcher 负责分发和处理Action,包括提交、暂停、继续、取消、网络状态变化、重试等等。加载完成后把结果在转发给Picasso来显示图片。整个调度流程头尾全由Picasso控制。
//分析调用,代码有省略。
Picasso类
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}
Picasso类
public RequestCreator load(String path) {
***
return load(Uri.parse(path));
}
Picasso类
public RequestCreator load(Uri uri) {
return new RequestCreator(this, uri, 0);
}
RequestCreator类
RequestCreator(Picasso picasso, Uri uri, int resourceId) {
this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}
RequestCreator类
public void into(ImageView target, Callback callback) {
Request request = createRequest(started);
String requestKey = createKey(request);
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);
}
Picasso类
void enqueueAndSubmit(Action action) {
submit(action);
}
void submit(Action action) {
dispatcher.dispatchSubmit(action);
}
Dispatcher类
void dispatchSubmit(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
handler的handleMessage处理发送的数据
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
}
}
void performSubmit(Action action) {
performSubmit(action, true);
}
void performSubmit(Action action, boolean dismissFailed) {
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
//到此步,就把请求提交给线程池了。
hunter.future = service.submit(hunter);
hunterMap.put(action.getKey(), hunter);
}
Picasso的HANDLER处理返回的数据
@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;
}
void complete(BitmapHunter hunter) {
if (single != null) {
deliverAction(result, from, single);
}
}
private void deliverAction(Bitmap result, LoadedFrom from, Action action) {
action.complete(result, from);
}
ImageViewAction类
@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
}
最后一步显示到Imageview上
static void setBitmap(ImageView target, Context context, Bitmap bitmap,
Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
Drawable placeholder = target.getDrawable();
if (placeholder instanceof AnimationDrawable) {
((AnimationDrawable) placeholder).stop();
}
PicassoDrawable drawable =
new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
target.setImageDrawable(drawable);
}
二,细节分析
Picasso使用单例+Builder模式创建对象。
单例使用双重校验锁(DCL)实现,使用volatile的作用在于DCL实现单例还是存在一定的问题。
单例模式详解:如何正确地写出单例模式
static volatile Picasso singleton = null;
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}
Picasso会将传入的Context转为ApplicationContext,防止引起Activity和fragment内存泄露。
public Builder(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
public Picasso build() {
Context context = this.context;
//1.downloader
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
//2.cache
if (cache == null) {
cache = new LruCache(context);
}
//3.service
if (service == null) {
service = new PicassoExecutorService();
}
//4.transformer
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}
//5.stats
Stats stats = new Stats(cache);
//6.dispatcher
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
2.1 Downloader
Downloader内置两个子类,UrlConnectionDownloader和OkHttpDownloader。
网络图片下载器,如果项目中有OkHttp就使用,没有就使用HttpURLConnection。源码版本2.5.2,不能反射到okhttp3,所以使用okhttp3作为Picasso的网络请求需要写一个支持okhttp3的Downloader。当然,肯定有人已经写过啦。JakeWharton大神的picasso2-okhttp3-downloader就是实现了这个功能。继续撸码go go。
static Downloader createDefaultDownloader(Context context) {
try {
//反射
Class.forName("com.squareup.okhttp.OkHttpClient");
return OkHttpLoaderCreator.create(context);
} catch (ClassNotFoundException ignored) {
}
return new UrlConnectionDownloader(context);
}
//OkHttpDownloader,最终会调用该构造方法
public OkHttpDownloader(final File cacheDir, final long maxSize) {
//new了一个OkHttpClient,连接超时默认15s,读写超时默认20s。
this(defaultOkHttpClient());
try {
client.setCache(new com.squareup.okhttp.Cache(cacheDir, maxSize));
} catch (IOException ignored) {
}
}
//默认缓存文件,这就是硬盘缓存。
//硬盘缓存使用的是网络框架的缓存,Picasso自己没有设计硬盘缓存。只有内存缓存(LruCache)
static File createDefaultCacheDir(Context context) {
File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);
if (!cache.exists()) {
//noinspection ResultOfMethodCallIgnored
cache.mkdirs();
}
return cache;
}
//计算硬盘能使用缓存空间的大小,返回数据size为该文件能使用的总空间的2%。
//但是最大只能50M,最小5M。
private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB
private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
static long calculateDiskCacheSize(File dir) {
long size = MIN_DISK_CACHE_SIZE;
try {
//get如何计算指定文件的大小。
StatFs statFs = new StatFs(dir.getAbsolutePath());
long available = ((long) statFs.getBlockCount()) * statFs.getBlockSize();
// Target 2% of the total space.
size = available / 50;
} catch (IllegalArgumentException ignored) {
}
// Bound inside min/max size for disk cache.
return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);
}
继续分析下UrlConnectionDownloader,在SDK14以后,
也会设置硬盘缓存,用HttpResponseCache去实现的。缓存文件路径和大小和Okhttp相同。
@Override public Response load(Uri uri, int networkPolicy) throws IOException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
installCacheIfNeeded(context);
}
}
private static void installCacheIfNeeded(Context context) {
// DCL + volatile should be safe after Java 5.
if (cache == null) {
try {
synchronized (lock) {
if (cache == null) {
cache = ResponseCacheIcs.install(context);
}
}
} catch (IOException ignored) {
}
}
}
private static class ResponseCacheIcs {
static Object install(Context context) throws IOException {
File cacheDir = Utils.createDefaultCacheDir(context);
HttpResponseCache cache = HttpResponseCache.getInstalled();
if (cache == null) {
long maxSize = Utils.calculateDiskCacheSize(cacheDir);
cache = HttpResponseCache.install(cacheDir, maxSize);
}
return cache;
}
static void close(Object cache) {
try {
((HttpResponseCache) cache).close();
} catch (IOException ignored) {
}
}
}
2.2 LruCache
LRU算法主要依靠LinkedHashMap实现。用系统给应用分配内存的15%做缓存。在每次set之后都会trimToSize,当size满了就会开始移除数据。
public LruCache(Context context) {
this(Utils.calculateMemoryCacheSize(context));
}
static int calculateMemoryCacheSize(Context context) {
ActivityManager am = getService(context, ACTIVITY_SERVICE);
boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
//getMemoryClass()是系统为应用分配的内存。
int memoryClass = am.getMemoryClass();
if (largeHeap && SDK_INT >= HONEYCOMB) {
//manifest文件中的<application>标签中largeHeap属性设置为true,获得应用可使用的最大内存,不建议使用
memoryClass = ActivityManagerHoneycomb.getLargeMemoryClass(am);
}
// Target ~15% of the available heap. 获取~15%内存
return 1024 * 1024 * memoryClass / 7;
}
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("Max size must be positive.");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
}
@Override public void set(String key, Bitmap bitmap) {
if (key == null || bitmap == null) {
throw new NullPointerException("key == null || bitmap == null");
}
Bitmap previous;
synchronized (this) {
putCount++;
size += Utils.getBitmapBytes(bitmap);
previous = map.put(key, bitmap);
if (previous != null) {
size -= Utils.getBitmapBytes(previous);
}
}
trimToSize(maxSize);
}
private void trimToSize(int maxSize) {
while (true) {
String key;
Bitmap value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(
getClass().getName() + ".sizeOf() is reporting inconsistent results!");
}
//还有缓存空间直接返回。
if (size <= maxSize || map.isEmpty()) {
break;
}
Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
//取出最近最少使用的数据从map中移除。
map.remove(key);
//把使用的空间减少。
size -= Utils.getBitmapBytes(value);
evictionCount++;
}
}
}
2.3 PicassoExecutorService
线程池,adjustThreadCount方法根据网络情况设置线程数数量,默认3个,wifi下4个,4G下3个,3G下2个,2G下1个。
class PicassoExecutorService extends ThreadPoolExecutor {
//默认线程数3个
private static final int DEFAULT_THREAD_COUNT = 3;
//核心线程和最大线程数相同,空闲线程等待时间0s。
PicassoExecutorService() {
super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
}
@Override
public Future<?> submit(Runnable task) {
//提交进来的task是BitmapHunter(Dispatcher里的performSubmit方法
//hunter.future = service.submit(hunter);) 所以直接转。然后交给execute(),返回Future
PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
execute(ftask);
return ftask;
}
private static final class PicassoFutureTask extends FutureTask<BitmapHunter>
implements Comparable<PicassoFutureTask> {
private final BitmapHunter hunter;
public PicassoFutureTask(BitmapHunter hunter) {
super(hunter, null);
this.hunter = hunter;
}
@Override
public int compareTo(PicassoFutureTask other) {
//排序
Picasso.Priority p1 = hunter.getPriority();
Picasso.Priority p2 = other.hunter.getPriority();
// High-priority requests are "lesser" so they are sorted to the front.
//高优先级需要更小,排在前面
// Equal priorities are sorted by sequence number to provide FIFO ordering.
//相同优先级就先进先出。
return (p1 == p2 ? hunter.sequence - other.hunter.sequence : p2.ordinal() - p1.ordinal());
}
}
}
2.4 RequestTransformer
请求转换。默认不改变,
官方举例用途,如果使用CDN时,可以根据用户地址改变主机来加快图片访问速度
if you use a CDN you can change the hostname for the image based on the current location of the user in order to get faster download speeds.
调用时机,在RequestCreator的into方法创建Request时
private Request createRequest(long started) {
int id = nextId.getAndIncrement();
Request request = data.build();
request.id = id;
request.started = started;
boolean loggingEnabled = picasso.loggingEnabled;
if (loggingEnabled) {
log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());
}
Request transformed = picasso.transformRequest(request);
if (transformed != request) {
// If the request was changed, copy over the id and timestamp from the original.
transformed.id = id;
transformed.started = started;
if (loggingEnabled) {
log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);
}
}
return transformed;
}
2.5 Stats
Stats的创建,用于统计缓存,缓存命中数,缓存丢失数。代码不长,全贴了。
class Stats {
private static final int CACHE_HIT = 0;
private static final int CACHE_MISS = 1;
private static final int BITMAP_DECODE_FINISHED = 2;
private static final int BITMAP_TRANSFORMED_FINISHED = 3;
private static final int DOWNLOAD_FINISHED = 4;
private static final String STATS_THREAD_NAME = Utils.THREAD_PREFIX + "Stats";
final HandlerThread statsThread;
final Cache cache;
final Handler handler;
//记录的数据,缓存的命中数,丢失数,总下载大小,总原始bitmap大小等等。见名知意,安逸得很。
long cacheHits;
long cacheMisses;
long totalDownloadSize;
long totalOriginalBitmapSize;
long totalTransformedBitmapSize;
long averageDownloadSize;
long averageOriginalBitmapSize;
long averageTransformedBitmapSize;
int downloadCount;
int originalBitmapCount;
int transformedBitmapCount;
Stats(Cache cache) {
this.cache = cache;
//这里非常契合HandlerThread使用场景。单独线程去记录缓存的情况,处理各种不同情况的消息。
this.statsThread = new HandlerThread(STATS_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
//不要忘记start
this.statsThread.start();
//细节方法。按注释的意思是在Android 5以前,HandlerThread会持有最后一个消息的引用
//为了处理这种情况,每秒都会发送一个空的新消息给handler,细节感动。
Utils.flushStackLocalLeaks(statsThread.getLooper());
this.handler = new StatsHandler(statsThread.getLooper(), this);
}
void dispatchBitmapDecoded(Bitmap bitmap) {
processBitmap(bitmap, BITMAP_DECODE_FINISHED);
}
void dispatchBitmapTransformed(Bitmap bitmap) {
processBitmap(bitmap, BITMAP_TRANSFORMED_FINISHED);
}
void dispatchDownloadFinished(long size) {
handler.sendMessage(handler.obtainMessage(DOWNLOAD_FINISHED, size));
}
void dispatchCacheHit() {
handler.sendEmptyMessage(CACHE_HIT);
}
void dispatchCacheMiss() {
handler.sendEmptyMessage(CACHE_MISS);
}
void shutdown() {
statsThread.quit();
}
void performCacheHit() {
cacheHits++;
}
void performCacheMiss() {
cacheMisses++;
}
void performDownloadFinished(Long size) {
downloadCount++;
totalDownloadSize += size;
averageDownloadSize = getAverage(downloadCount, totalDownloadSize);
}
void performBitmapDecoded(long size) {
originalBitmapCount++;
totalOriginalBitmapSize += size;
averageOriginalBitmapSize = getAverage(originalBitmapCount, totalOriginalBitmapSize);
}
void performBitmapTransformed(long size) {
transformedBitmapCount++;
totalTransformedBitmapSize += size;
averageTransformedBitmapSize = getAverage(originalBitmapCount, totalTransformedBitmapSize);
}
StatsSnapshot createSnapshot() {
return new StatsSnapshot(cache.maxSize(), cache.size(), cacheHits, cacheMisses,
totalDownloadSize, totalOriginalBitmapSize, totalTransformedBitmapSize, averageDownloadSize,
averageOriginalBitmapSize, averageTransformedBitmapSize, downloadCount, originalBitmapCount,
transformedBitmapCount, System.currentTimeMillis());
}
private void processBitmap(Bitmap bitmap, int what) {
// Never send bitmaps to the handler as they could be recycled before we process them.
int bitmapSize = Utils.getBitmapBytes(bitmap);
handler.sendMessage(handler.obtainMessage(what, bitmapSize, 0));
}
private static long getAverage(int count, long totalSize) {
return totalSize / count;
}
private static class StatsHandler extends Handler {
private final Stats stats;
public StatsHandler(Looper looper, Stats stats) {
super(looper);
this.stats = stats;
}
//handleMessage对各种消息的处理。后面我们在看从哪里发出来的消息。
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case CACHE_HIT:
stats.performCacheHit();
break;
case CACHE_MISS:
stats.performCacheMiss();
break;
case BITMAP_DECODE_FINISHED:
stats.performBitmapDecoded(msg.arg1);
break;
case BITMAP_TRANSFORMED_FINISHED:
stats.performBitmapTransformed(msg.arg1);
break;
case DOWNLOAD_FINISHED:
stats.performDownloadFinished((Long) msg.obj);
break;
default:
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new AssertionError("Unhandled stats message." + msg.what);
}
});
}
}
}
2.6 Dispatcher
调度器,调度任务。
class Dispatcher {
//构造方法,传入前面生成的一些参数。这个mainThreadHandler是
//Picasso类中的一个handler,图片下载成功后,Dispatcher会给这个handler发消息。
Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
Downloader downloader, Cache cache, Stats stats) {
//HandlerThread子类
this.dispatcherThread = new DispatcherThread();
this.dispatcherThread.start();
Utils.flushStackLocalLeaks(dispatcherThread.getLooper());
this.context = context;
this.service = service;
this.hunterMap = new LinkedHashMap<String, BitmapHunter>();
this.failedActions = new WeakHashMap<Object, Action>();
this.pausedActions = new WeakHashMap<Object, Action>();
this.pausedTags = new HashSet<Object>();
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
this.downloader = downloader;
this.mainThreadHandler = mainThreadHandler;
this.cache = cache;
this.stats = stats;
this.batch = new ArrayList<BitmapHunter>(4);
this.airplaneMode = Utils.isAirplaneModeOn(this.context);
this.scansNetworkChanges = hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);
this.receiver = new NetworkBroadcastReceiver(this);
receiver.register();
}
//这个类和Stats套路一样,用HandlerThread配合Handler调度处理各种请求。我们直接看handleMessage了。
//消息来源主要在Picasso类中,比如分析的submit(),cancelExistingRequest(),pauseTag(),resumeTag()。
}
private static class DispatcherHandler extends Handler {
private final Dispatcher dispatcher;
public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
super(looper);
this.dispatcher = dispatcher;
}
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
}
case REQUEST_CANCEL: {
Action action = (Action) msg.obj;
dispatcher.performCancel(action);
break;
}
case TAG_PAUSE: {
Object tag = msg.obj;
dispatcher.performPauseTag(tag);
break;
}
case TAG_RESUME: {
Object tag = msg.obj;
dispatcher.performResumeTag(tag);
break;
}
case HUNTER_COMPLETE: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performComplete(hunter);
break;
}
case HUNTER_RETRY: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performRetry(hunter);
break;
}
case HUNTER_DECODE_FAILED: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performError(hunter, false);
break;
}
case HUNTER_DELAY_NEXT_BATCH: {
dispatcher.performBatchComplete();
break;
}
case NETWORK_STATE_CHANGE: {
NetworkInfo info = (NetworkInfo) msg.obj;
dispatcher.performNetworkStateChange(info);
break;
}
case AIRPLANE_MODE_CHANGE: {
dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON);
break;
}
default:
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new AssertionError("Unknown handler message received: " + msg.what);
}
});
}
}
}
看看performSubmit方法
void performSubmit(Action action, boolean dismissFailed) {
//1.先看这条Action 是否被标记暂停了。标记了就直接放进pausedActions里。有日志的话就打印日志出来。
if (pausedTags.contains(action.getTag())) {
pausedActions.put(action.getTarget(), action);
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
"because tag '" + action.getTag() + "' is paused");
}
return;
}
//2.再看hunterMap是否有这个Action,有了就让hunter attach到这个Action。
BitmapHunter hunter = hunterMap.get(action.getKey());
if (hunter != null) {
hunter.attach(action);
return;
}
//3.再看PicassoService是否被关了。
if (service.isShutdown()) {
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
}
return;
}
//这句话就必须撸下BitmapHunter了
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
//把BitmapHunter提交的线程池里
hunter.future = service.submit(hunter);
//把hunter放进hunterMap里
hunterMap.put(action.getKey(), hunter);
//是否移除失败的Action
if (dismissFailed) {
failedActions.remove(action.getTarget());
}
//打印request的logID
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
}
}
跳出来看下RequestHandler,BitmapHunter
//在Picasso构造方法里有这段代码,默认的给每个Action都添加的7个RequestHandler的实现类。
//有处理Resource资源的ResourceRequestHandler
//Asset资源的AssetRequestHandler
//网络资源的NetworkRequestHandler
int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
List<RequestHandler> allRequestHandlers =
new ArrayList<RequestHandler>(builtInHandlers + extraCount);
// ResourceRequestHandler needs to be the first in the list to avoid
// forcing other RequestHandlers to perform null checks on request.uri
// to cover the (request.resourceId != 0) case.
allRequestHandlers.add(new ResourceRequestHandler(context));
if (extraRequestHandlers != null) {
allRequestHandlers.addAll(extraRequestHandlers);
}
allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
allRequestHandlers.add(new MediaStoreRequestHandler(context));
allRequestHandlers.add(new ContentStreamRequestHandler(context));
allRequestHandlers.add(new AssetRequestHandler(context));
allRequestHandlers.add(new FileRequestHandler(context));
allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
//设置了就不可以更改。
requestHandlers = Collections.unmodifiableList(allRequestHandlers);
这里以NetworkRequestHandler为例看一下
class NetworkRequestHandler extends RequestHandler {
static final int RETRY_COUNT = 2;
private static final String SCHEME_HTTP = "http";
private static final String SCHEME_HTTPS = "https";
private final Downloader downloader;
private final Stats stats;
//构造方法,需要一个downloader和stats
public NetworkRequestHandler(Downloader downloader, Stats stats) {
this.downloader = downloader;
this.stats = stats;
}
//是否可以处理这个Request,如果这个Request的uri的scheme是HTTP或者HTTPS就可以处理
@Override public boolean canHandleRequest(Request data) {
String scheme = data.uri.getScheme();
return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
}
//加载方法,response 由downloader.load()去下载
//我们平常自己写个UrlConnection去加载图片的套路不就是在个
//子线程里开启HttpURLConnection.openConnection(),然后
//connection.getInputStream()。再把Stream转成Bitmap嘛。
//downloader我们前面看了。就是这个原理。当然别个牛皮太多了。
//这里就是网络请求,在把放到一个子线程里就行了,
//BitmapHunter就是这个子线程的Runable
@Override public Result load(Request request, int networkPolicy) throws IOException {
Response response = downloader.load(request.uri, request.networkPolicy);
if (response == null) {
return null;
}
//是否由缓存中加载的
Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
Bitmap bitmap = response.getBitmap();
if (bitmap != null) {
return new Result(bitmap, loadedFrom);
}
InputStream is = response.getInputStream();
if (is == null) {
return null;
}
// Sometimes response content length is zero when requests are being replayed. Haven't found
// root cause to this but retrying the request seems safe to do so.
if (loadedFrom == DISK && response.getContentLength() == 0) {
Utils.closeQuietly(is);
throw new ContentLengthException("Received response with 0 content-length header.");
}
//通知Stats计数了。
if (loadedFrom == NETWORK && response.getContentLength() > 0) {
stats.dispatchDownloadFinished(response.getContentLength());
}
return new Result(is, loadedFrom);
}
@Override int getRetryCount() {
return RETRY_COUNT;
}
@Override boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
return info == null || info.isConnected();
}
@Override boolean supportsReplay() {
return true;
}
static class ContentLengthException extends IOException {
public ContentLengthException(String message) {
super(message);
}
}
}
class BitmapHunter implements Runnable {
//构造方法
BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action,
RequestHandler requestHandler) {
this.sequence = SEQUENCE_GENERATOR.incrementAndGet();
this.picasso = picasso;
this.dispatcher = dispatcher;
this.cache = cache;
this.stats = stats;
this.action = action;
this.key = action.getKey();
this.data = action.getRequest();
this.priority = action.getPriority();
this.memoryPolicy = action.getMemoryPolicy();
this.networkPolicy = action.getNetworkPolicy();
this.requestHandler = requestHandler;
this.retryCount = requestHandler.getRetryCount();
}
//刚刚的forRequest方法
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
Action action) {
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
// Index-based loop to avoid allocating an iterator.
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
//找到一个可以处理这个Request类型的RequestHandler
if (requestHandler.canHandleRequest(request)) {
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
//找不到就抛异常
return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
}
//Runable最重要的就是run方法了
@Override public void run() {
try {
updateThreadName(data);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
}
//得到结果
result = hunt();
if (result == null) {
//分发失败消息
dispatcher.dispatchFailed(this);
} else {
//分发成功消息
dispatcher.dispatchComplete(this);
}
} catch (Downloader.ResponseException e) {
if (!e.localCacheOnly || e.responseCode != 504) {
exception = e;
}
dispatcher.dispatchFailed(this);
} catch (NetworkRequestHandler.ContentLengthException e) {
exception = e;
dispatcher.dispatchRetry(this);
} catch (IOException e) {
exception = e;
dispatcher.dispatchRetry(this);
} catch (OutOfMemoryError e) {
StringWriter writer = new StringWriter();
stats.createSnapshot().dump(new PrintWriter(writer));
exception = new RuntimeException(writer.toString(), e);
dispatcher.dispatchFailed(this);
} catch (Exception e) {
exception = e;
dispatcher.dispatchFailed(this);
} finally {
Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
}
}
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
//先去内存中找
if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
}
return bitmap;
}
}
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
//这里调用了RequestHandler的load方法,返回了Result
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
loadedFrom = result.getLoadedFrom();
exifRotation = result.getExifOrientation();
bitmap = result.getBitmap();
// If there was no Bitmap then we need to decode it from the stream.
if (bitmap == null) {
InputStream is = result.getStream();
try {
bitmap = decodeStream(is, data);
} finally {
Utils.closeQuietly(is);
}
}
}
if (bitmap != null) {
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId());
}
stats.dispatchBitmapDecoded(bitmap);
if (data.needsTransformation() || exifRotation != 0) {
synchronized (DECODE_LOCK) {
//图片是否需要转换处理。
if (data.needsMatrixTransform() || exifRotation != 0) {
//先是自带的转换,比如是否要转个角度啊,是否要centerCrop啊等。
bitmap = transformResult(data, bitmap, exifRotation);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
}
}
if (data.hasCustomTransformations()) {
//再来自定义的转换。后面来些实例。
bitmap = applyCustomTransformations(data.transformations, bitmap);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
}
}
}
if (bitmap != null) {
stats.dispatchBitmapTransformed(bitmap);
}
}
}
return bitmap;
}
}
接下来看下dispatcher.performComplete(hunter)方法。在BitmapHunter中run方法result不为空就发送complete消息。就会调用此方法
void performComplete(BitmapHunter hunter) {
//判断是否写到内存
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
cache.set(hunter.getKey(), hunter.getResult());
}
//把这个hunter可以移除了。
hunterMap.remove(hunter.getKey());
batch(hunter);
if (hunter.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
}
}
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
batch.add(hunter);
//如果没有这个消息就发
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
//等200ms发送消息
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
//调用这个方法,原来是把这段时间的hunter一起返回。用Picasso时是不是几张几张图片一起显示,
//不是一张一张的加载,细节优化。完美
//Dispatcher回调给Picasso,发送消息把结果带回去。
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
Picasso的handler处理COMPLETE。
complete->deliverAction->action.complete()
我们这是ImageViewAction,看看
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
调用PicassoDrawable的setBitmap完成最后一步
static void setBitmap(ImageView target, Context context, Bitmap bitmap,
Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
Drawable placeholder = target.getDrawable();
if (placeholder instanceof AnimationDrawable) {
((AnimationDrawable) placeholder).stop();
}
PicassoDrawable drawable =
new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
target.setImageDrawable(drawable);
}
网友评论