picasso是Square公司开源的一个Android图形缓存库

主要有以下一些特性:

在adapter中回收和取消当前的下载;

使用最少的内存完成复杂的图形转换操作;

自动的内存和硬盘缓存;

图形转换操作,如变换大小,旋转等,提供了接口来让用户可以自定义转换操作;

加载载网络或本地资源;

Picasso.class

他有一个内部类,一般是通过他来创建实例的:

downloader(Downloader downloader) :

容许使用自定义的下载器,可以用okhttp或者volley,必须实现Downloader接口。

executor(ExecutorService executorService):

容许使用自己的线程池来进行下载

memoryCache(Cache memoryCache):

容许使用自己的缓存类,必须实现Cache接口。

requestTransformer(RequestTransformer transformer):

listener(Listener listener):

addRequestHandler(RequestHandler requestHandler):

indicatorsEnabled(boolean enabled):设置图片来源的指示器。

loggingEnabled(boolean enabled):

再来看build()方法:

  1. /** Create the {@link Picasso} instance. */
  2. public Picasso build() {
  3. Context context = this.context;
  4. if (downloader == null) {
  5. downloader = Utils.createDefaultDownloader(context);
  6. }
  7. if (cache == null) {
  8. cache = new LruCache(context);
  9. }
  10. if (service == null) {
  11. service = new PicassoExecutorService();
  12. }
  13. if (transformer == null) {
  14. transformer = RequestTransformer.IDENTITY;
  15. }
  16. Stats stats = new Stats(cache);
  17. Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
  18. return new Picasso(context, dispatcher, cache, listener, transformer,
  19. requestHandlers, stats, indicatorsEnabled, loggingEnabled);
  20. }

这里会使用默认的下载器,缓存类,线程池,并将这些对象封装到了分发器Dispatcher里。然后在返回一个Picasso对象。

一般情况下,如果不需要自定义bulid里的这些方法,可以使用Picasso.with(context)默认方法来获得单例对象:

  1. public static Picasso with(Context context) {
  2. if (singleton == null) {
  3. synchronized (Picasso.class) {
  4. if (singleton == null) {
  5. singleton = new Builder(context).build();
  6. }
  7. }
  8. }
  9. return singleton;
  10. }

如果需要自定义一些对象:

  1. public class SamplePicassoFactory {
  2. private static Picasso sPicasso;
  3. public static Picasso getPicasso(Context context) {
  4. if (sPicasso == null) {
  5. sPicasso = new Picasso.Builder(context)
  6. .downloader(new OkHttpDownloader(context, ConfigConstants.MAX_DISK_CACHE_SIZE))
  7. .memoryCache(new LruCache(ConfigConstants.MAX_MEMORY_CACHE_SIZE))
  8. .build();
  9. }
  10. return sPicasso;
  11. }
  12. }

通过上面的方法,获得sPicasso对象后就设置成了单例,但是最好设置成双重校验锁模式。

如果通过以上方法获得对象后,还可以通过Picasso.setSingletonInstance(Picasso picasso)方法设置对象到Picasso中,这样以后还是通过Picasso.with(context)来调用。

接着就可以通过以下方式设置加载源:

可以是uri地址,file文件,res资源drawable。

最终都是通过以下方法来创建一个RequestCreator对象:

  1. public RequestCreator load(Uri uri) {
  2. return new RequestCreator(this, uri, 0);
  3. }
  1. RequestCreator(Picasso picasso, Uri uri, int resourceId) {
  2. if (picasso.shutdown) {
  3. throw new IllegalStateException(
  4. "Picasso instance already shut down. Cannot submit new requests.");
  5. }
  6. this.picasso = picasso;
  7. this.data = new Request.Builder(uri, resourceId);
  8. }

RequestCreator对象是用来设置一系列属性的,如:


noPlaceholder():设置没有加载等待图片

placeholder(int placeholderResId):设置加载等待图片

placeholder(Drawable placeholderDrawable):设置加载等待图片

error(int errorResId):设置加载出错的图片。

error(Drawable errorDrawable):设置加载出错的图片。

tag(Object tag):设置标记

fit():自适应,下载的图片有多少像素就显示多少像素

resizeDimen(int targetWidthResId, int targetHeightResId):设置图片显示的像素。

resize(int targetWidth, int targetHeight):设置图片显示的像素。

centerCrop():设置ImageView的ScaleType属性.

centerInside():设置ImageView的ScaleType属性.

rotate(float degrees):设置旋转角度。

rotate(float degrees, float pivotX, float pivotY):设置以某个中心点设置某个旋转角度。

config(Bitmap.Config config):设置Bitmap的Config属性

priority(Priority priority):设置请求的优先级。

transform(Transformation transformation):

skipMemoryCache():跳过内存缓存

memoryPolicy(MemoryPolicy policy, MemoryPolicy... additional):

networkPolicy(NetworkPolicy policy, NetworkPolicy... additional):

noFade():没有淡入淡出效果

get():获得bitmap对象

fetch():

设置完以上一系列属性之后,最关键的就是into方法,现在以into(ImageView target)举例:

  1. public void into(ImageView target) {
  2. into(target, null);
  3. }

他实际调用的是:

  1. public void into(ImageView target, Callback callback) {
  2. long started = System.nanoTime();
  3. checkMain();
  4. if (target == null) {
  5. throw new IllegalArgumentException("Target must not be null.");
  6. }
  7. if (!data.hasImage()) {
  8. picasso.cancelRequest(target);
  9. if (setPlaceholder) {
  10. setPlaceholder(target, getPlaceholderDrawable());
  11. }
  12. return;
  13. }
  14. if (deferred) {
  15. if (data.hasSize()) {
  16. throw new IllegalStateException("Fit cannot be used with resize.");
  17. }
  18. int width = target.getWidth();
  19. int height = target.getHeight();
  20. if (width == 0 || height == 0) {
  21. if (setPlaceholder) {
  22. setPlaceholder(target, getPlaceholderDrawable());
  23. }
  24. picasso.defer(target, new DeferredRequestCreator(this, target, callback));
  25. return;
  26. }
  27. data.resize(width, height);
  28. }
  29. Request request = createRequest(started);
  30. String requestKey = createKey(request);
  31. if (shouldReadFromMemoryCache(memoryPolicy)) {
  32. Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
  33. if (bitmap != null) {
  34. picasso.cancelRequest(target);
  35. setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
  36. if (picasso.loggingEnabled) {
  37. log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
  38. }
  39. if (callback != null) {
  40. callback.onSuccess();
  41. }
  42. return;
  43. }
  44. }
  45. if (setPlaceholder) {
  46. setPlaceholder(target, getPlaceholderDrawable());
  47. }
  48. Action action =
  49. new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
  50. errorDrawable, requestKey, tag, callback, noFade);
  51. picasso.enqueueAndSubmit(action);
  52. }
  1. checkMain();

首先检查是否是主线程

  1. if (target == null) {
  2. throw new IllegalArgumentException("Target must not be null.");
  3. }

检查目标view是否存在

  1. if (!data.hasImage()) {
  2. picasso.cancelRequest(target);
  3. if (setPlaceholder) {
  4. setPlaceholder(target, getPlaceholderDrawable());
  5. }
  6. return;
  7. }

如果没有设置uri或者resDrawable,就停止请求,如果设置了加载等待的图片就设置,然后就return

  1. if (deferred) {
  2. if (data.hasSize()) {
  3. throw new IllegalStateException("Fit cannot be used with resize.");
  4. }
  5. int width = target.getWidth();
  6. int height = target.getHeight();
  7. if (width == 0 || height == 0) {
  8. if (setPlaceholder) {
  9. setPlaceholder(target, getPlaceholderDrawable());
  10. }
  11. picasso.defer(target, new DeferredRequestCreator(this, target, callback));
  12. return;
  13. }
  14. data.resize(width, height);
  15. }

如果设置fit自适应:如果已经设置了图片像素大小就抛异常,如果目标view的长宽等于0,就在设置等待图片后延期处理,如果不等于0就设置size到Request.Builder的data里。

  1. Request request = createRequest(started);
  2. String requestKey = createKey(request);

接着就创建Request,并生成一个String类型的requestKey。

  1. /** Create the request optionally passing it through the request transformer. */
  2. private Request createRequest(long started) {
  3. int id = nextId.getAndIncrement();
  4. Request request = data.build();
  5. request.id = id;
  6. request.started = started;
  7. boolean loggingEnabled = picasso.loggingEnabled;
  8. if (loggingEnabled) {
  9. log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());
  10. }
  11. Request transformed = picasso.transformRequest(request);
  12. if (transformed != request) {
  13. // If the request was changed, copy over the id and timestamp from the original.
  14. transformed.id = id;
  15. transformed.started = started;
  16. if (loggingEnabled) {
  17. log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);
  18. }
  19. }
  20. return transformed;
  21. }

在以上代码可以看出,这里调用picasso.transformRequest(request);来给使用者提供一个可以更改request的机会,他最终调用的是Picasso.Build里面的通过requestTransformer(RequestTransformer transformer)方法传进去的RequestTransformer 接口:

  1. public interface RequestTransformer {
  2. /**
  3. * Transform a request before it is submitted to be processed.
  4. *
  5. * @return The original request or a new request to replace it. Must not be null.
  6. */
  7. Request transformRequest(Request request);
  8. /** A {@link RequestTransformer} which returns the original request. */
  9. RequestTransformer IDENTITY = new RequestTransformer() {
  10. @Override public Request transformRequest(Request request) {
  11. return request;
  12. }
  13. };
  14. }

默认使用的是IDENTITY。这里没有做任何的修改,如果有需要可以自己设置接口以达到修改Request的目的。

  1. if (shouldReadFromMemoryCache(memoryPolicy)) {
  2. Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
  3. if (bitmap != null) {
  4. picasso.cancelRequest(target);
  5. setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
  6. if (picasso.loggingEnabled) {
  7. log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
  8. }
  9. if (callback != null) {
  10. callback.onSuccess();
  11. }
  12. return;
  13. }
  14. }

接着要判断是否需要从缓存中读取图片,如果需要,就要根据requestKey从缓存中读取:

  1. Bitmap quickMemoryCacheCheck(String key) {
  2. Bitmap cached = cache.get(key);
  3. if (cached != null) {
  4. stats.dispatchCacheHit();
  5. } else {
  6. stats.dispatchCacheMiss();
  7. }
  8. return cached;
  9. }

这里的cache用的是LruCache内存缓存类,这个内存缓存类,实现了Cache接口,最后调用的是:

  1. @Override public Bitmap get(String key) {
  2. if (key == null) {
  3. throw new NullPointerException("key == null");
  4. }
  5. Bitmap mapValue;
  6. synchronized (this) {
  7. mapValue = map.get(key);
  8. if (mapValue != null) {
  9. hitCount++;
  10. return mapValue;
  11. }
  12. missCount++;
  13. }
  14. return null;
  15. }

这里的map是LinkedHashMap<String, Bitmap>:

  1. this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);

缓存的策略稍后分析,现在先回到前面,如果从缓存中拿到的bitmap不等于null,就调用picasso.cancelRequest(target)来删除请求,然后通过

  1. setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);

设置图片,然后如果设置了callback回调,再回掉callback.onSuccess();方法,然后就return。

如果没有设置内存缓存,或者缓存中的图片已经不存在:

  1. if (setPlaceholder) {
  2. setPlaceholder(target, getPlaceholderDrawable());
  3. }
  4. Action action =
  5. new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
  6. errorDrawable, requestKey, tag, callback, noFade);
  7. picasso.enqueueAndSubmit(action);

接着就设置等待加载图片,然后封装一个action,然后将action加入到分发器中:

  1. void enqueueAndSubmit(Action action) {
  2. Object target = action.getTarget();
  3. if (target != null && targetToAction.get(target) != action) {
  4. // This will also check we are on the main thread.
  5. cancelExistingRequest(target);
  6. targetToAction.put(target, action);
  7. }
  8. submit(action);
  9. }
  10. void submit(Action action) {
  11. dispatcher.dispatchSubmit(action);
  12. }

然后调用了dispatcher的performSubmit()方法:

  1. void performSubmit(Action action) {
  2. performSubmit(action, true);
  3. }
  4. void performSubmit(Action action, boolean dismissFailed) {
  5. if (pausedTags.contains(action.getTag())) {
  6. pausedActions.put(action.getTarget(), action);
  7. if (action.getPicasso().loggingEnabled) {
  8. log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
  9. "because tag '" + action.getTag() + "' is paused");
  10. }
  11. return;
  12. }
  13. BitmapHunter hunter = hunterMap.get(action.getKey());
  14. if (hunter != null) {
  15. hunter.attach(action);
  16. return;
  17. }
  18. if (service.isShutdown()) {
  19. if (action.getPicasso().loggingEnabled) {
  20. log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
  21. }
  22. return;
  23. }
  24. hunter = forRequest(action.getPicasso(), this, cache, stats, action);
  25. hunter.future = service.submit(hunter);
  26. hunterMap.put(action.getKey(), hunter);
  27. if (dismissFailed) {
  28. failedActions.remove(action.getTarget());
  29. }
  30. if (action.getPicasso().loggingEnabled) {
  31. log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
  32. }
  33. }

这里通过 hunter = forRequest(action.getPicasso(), this, cache, stats, action);获得一个BitmapHunter对象,接着就提交到线程池中 hunter.future = service.submit(hunter);:

接着线程池就会调用hunter 的run方法,因为这实现了Runnable对象:

  1. @Override public void run() {
  2. try {
  3. updateThreadName(data);
  4. if (picasso.loggingEnabled) {
  5. log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
  6. }
  7. result = hunt();
  8. if (result == null) {
  9. dispatcher.dispatchFailed(this);
  10. } else {
  11. dispatcher.dispatchComplete(this);
  12. }
  13. } catch (Downloader.ResponseException e) {
  14. if (!e.localCacheOnly || e.responseCode != 504) {
  15. exception = e;
  16. }
  17. dispatcher.dispatchFailed(this);
  18. } catch (NetworkRequestHandler.ContentLengthException e) {
  19. exception = e;
  20. dispatcher.dispatchRetry(this);
  21. } catch (IOException e) {
  22. exception = e;
  23. dispatcher.dispatchRetry(this);
  24. } catch (OutOfMemoryError e) {
  25. StringWriter writer = new StringWriter();
  26. stats.createSnapshot().dump(new PrintWriter(writer));
  27. exception = new RuntimeException(writer.toString(), e);
  28. dispatcher.dispatchFailed(this);
  29. } catch (Exception e) {
  30. exception = e;
  31. dispatcher.dispatchFailed(this);
  32. } finally {
  33. Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
  34. }
  35. }

接着调用hunt()方法:

  1. Bitmap hunt() throws IOException {
  2. Bitmap bitmap = null;
  3. if (shouldReadFromMemoryCache(memoryPolicy)) {
  4. bitmap = cache.get(key);
  5. if (bitmap != null) {
  6. stats.dispatchCacheHit();
  7. loadedFrom = MEMORY;
  8. if (picasso.loggingEnabled) {
  9. log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
  10. }
  11. return bitmap;
  12. }
  13. }
  14. data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
  15. RequestHandler.Result result = requestHandler.load(data, networkPolicy);
  16. if (result != null) {
  17. loadedFrom = result.getLoadedFrom();
  18. exifRotation = result.getExifOrientation();
  19. bitmap = result.getBitmap();
  20. // If there was no Bitmap then we need to decode it from the stream.
  21. if (bitmap == null) {
  22. InputStream is = result.getStream();
  23. try {
  24. bitmap = decodeStream(is, data);
  25. } finally {
  26. Utils.closeQuietly(is);
  27. }
  28. }
  29. }
  30. if (bitmap != null) {
  31. if (picasso.loggingEnabled) {
  32. log(OWNER_HUNTER, VERB_DECODED, data.logId());
  33. }
  34. stats.dispatchBitmapDecoded(bitmap);
  35. if (data.needsTransformation() || exifRotation != 0) {
  36. synchronized (DECODE_LOCK) {
  37. if (data.needsMatrixTransform() || exifRotation != 0) {
  38. bitmap = transformResult(data, bitmap, exifRotation);
  39. if (picasso.loggingEnabled) {
  40. log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
  41. }
  42. }
  43. if (data.hasCustomTransformations()) {
  44. bitmap = applyCustomTransformations(data.transformations, bitmap);
  45. if (picasso.loggingEnabled) {
  46. log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
  47. }
  48. }
  49. }
  50. if (bitmap != null) {
  51. stats.dispatchBitmapTransformed(bitmap);
  52. }
  53. }
  54. }
  55. return bitmap;
  56. }

这里如果从内存缓存中渠道bitmap对象,就直接返回了;否则通过requestHandler.load(data, networkPolicy);来发起网络请求(这个是NetworkRequestHandler类型的):

  1. @Override public Result load(Request request, int networkPolicy) throws IOException {
  2. Response response = downloader.load(request.uri, request.networkPolicy);
  3. if (response == null) {
  4. return null;
  5. }
  6. Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
  7. Bitmap bitmap = response.getBitmap();
  8. if (bitmap != null) {
  9. return new Result(bitmap, loadedFrom);
  10. }
  11. InputStream is = response.getInputStream();
  12. if (is == null) {
  13. return null;
  14. }
  15. // Sometimes response content length is zero when requests are being replayed. Haven't found
  16. // root cause to this but retrying the request seems safe to do so.
  17. if (loadedFrom == DISK && response.getContentLength() == 0) {
  18. Utils.closeQuietly(is);
  19. throw new ContentLengthException("Received response with 0 content-length header.");
  20. }
  21. if (loadedFrom == NETWORK && response.getContentLength() > 0) {
  22. stats.dispatchDownloadFinished(response.getContentLength());
  23. }
  24. return new Result(is, loadedFrom);
  25. }

这里调用了downloader.load(request.uri, request.networkPolicy)方法,这是一个UrlConnectionDownloader类型的对象,调用的是UrlConnectionDownloader的load()方法:

  1. @Override public Response load(Uri uri, int networkPolicy) throws IOException {
  2. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  3. installCacheIfNeeded(context);
  4. }
  5. HttpURLConnection connection = openConnection(uri);
  6. connection.setUseCaches(true);
  7. if (networkPolicy != 0) {
  8. String headerValue;
  9. if (NetworkPolicy.isOfflineOnly(networkPolicy)) {
  10. headerValue = FORCE_CACHE;
  11. } else {
  12. StringBuilder builder = CACHE_HEADER_BUILDER.get();
  13. builder.setLength(0);
  14. if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
  15. builder.append("no-cache");
  16. }
  17. if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
  18. if (builder.length() > 0) {
  19. builder.append(',');
  20. }
  21. builder.append("no-store");
  22. }
  23. headerValue = builder.toString();
  24. }
  25. connection.setRequestProperty("Cache-Control", headerValue);
  26. }
  27. int responseCode = connection.getResponseCode();
  28. if (responseCode >= 300) {
  29. connection.disconnect();
  30. throw new ResponseException(responseCode + " " + connection.getResponseMessage(),
  31. networkPolicy, responseCode);
  32. }
  33. long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
  34. boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));
  35. return new Response(connection.getInputStream(), fromCache, contentLength);
  36. }

如果系统版本是4.0以上,就使用installCacheIfNeeded(context)来启动data\data\包名\cacha下的某个目录的磁盘缓存:

  1. private static void installCacheIfNeeded(Context context) {
  2. // DCL + volatile should be safe after Java 5.
  3. if (cache == null) {
  4. try {
  5. synchronized (lock) {
  6. if (cache == null) {
  7. cache = ResponseCacheIcs.install(context);
  8. }
  9. }
  10. } catch (IOException ignored) {
  11. }
  12. }
  13. }
  14. private static class ResponseCacheIcs {
  15. static Object install(Context context) throws IOException {
  16. File cacheDir = Utils.createDefaultCacheDir(context);
  17. HttpResponseCache cache = HttpResponseCache.getInstalled();
  18. if (cache == null) {
  19. long maxSize = Utils.calculateDiskCacheSize(cacheDir);
  20. cache = HttpResponseCache.install(cacheDir, maxSize);
  21. }
  22. return cache;
  23. }

但是如果要是用这个磁盘缓存,就要在HttpURLConnection的响应头上加上缓存的控制头"Cache-Control"!

最后返回new Response(connection.getInputStream(), fromCache, contentLength)请求结果。

接着回到上面的hunt()方法的流程继续:

  1. if (result != null) {
  2. loadedFrom = result.getLoadedFrom();
  3. exifRotation = result.getExifOrientation();
  4. bitmap = result.getBitmap();
  5. // If there was no Bitmap then we need to decode it from the stream.
  6. if (bitmap == null) {
  7. InputStream is = result.getStream();
  8. try {
  9. bitmap = decodeStream(is, data);
  10. } finally {
  11. Utils.closeQuietly(is);
  12. }
  13. }
  14. }
  15. if (bitmap != null) {
  16. if (picasso.loggingEnabled) {
  17. log(OWNER_HUNTER, VERB_DECODED, data.logId());
  18. }
  19. stats.dispatchBitmapDecoded(bitmap);
  20. if (data.needsTransformation() || exifRotation != 0) {
  21. synchronized (DECODE_LOCK) {
  22. if (data.needsMatrixTransform() || exifRotation != 0) {
  23. bitmap = transformResult(data, bitmap, exifRotation);
  24. if (picasso.loggingEnabled) {
  25. log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
  26. }
  27. }
  28. if (data.hasCustomTransformations()) {
  29. bitmap = applyCustomTransformations(data.transformations, bitmap);
  30. if (picasso.loggingEnabled) {
  31. log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
  32. }
  33. }
  34. }
  35. if (bitmap != null) {
  36. stats.dispatchBitmapTransformed(bitmap);
  37. }
  38. }
  39. }
  40. return bitmap;

然后bitmap返回到run()方法里面的result应用上:

  1. if (result == null) {
  2. dispatcher.dispatchFailed(this);
  3. } else {
  4. dispatcher.dispatchComplete(this);
  5. }

如果result有结果就分发完成消息,最后将会调用到Picasso里面:

  1. static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
  2. @Override public void handleMessage(Message msg) {
  3. switch (msg.what) {
  4. case HUNTER_BATCH_COMPLETE: {
  5. @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
  6. //noinspection ForLoopReplaceableByForEach
  7. for (int i = 0, n = batch.size(); i < n; i++) {
  8. BitmapHunter hunter = batch.get(i);
  9. hunter.picasso.complete(hunter);
  10. }
  11. break;
  12. }
  1. void complete(BitmapHunter hunter) {
  2. Action single = hunter.getAction();
  3. List<Action> joined = hunter.getActions();
  4. boolean hasMultiple = joined != null && !joined.isEmpty();
  5. boolean shouldDeliver = single != null || hasMultiple;
  6. if (!shouldDeliver) {
  7. return;
  8. }
  9. Uri uri = hunter.getData().uri;
  10. Exception exception = hunter.getException();
  11. Bitmap result = hunter.getResult();
  12. LoadedFrom from = hunter.getLoadedFrom();
  13. if (single != null) {
  14. deliverAction(result, from, single);
  15. }
  16. if (hasMultiple) {
  17. //noinspection ForLoopReplaceableByForEach
  18. for (int i = 0, n = joined.size(); i < n; i++) {
  19. Action join = joined.get(i);
  20. deliverAction(result, from, join);
  21. }
  22. }
  23. if (listener != null && exception != null) {
  24. listener.onImageLoadFailed(this, uri, exception);
  25. }
  26. }

接着分发action:

  1. private void deliverAction(Bitmap result, LoadedFrom from, Action action) {
  2. if (action.isCancelled()) {
  3. return;
  4. }
  5. if (!action.willReplay()) {
  6. targetToAction.remove(action.getTarget());
  7. }
  8. if (result != null) {
  9. if (from == null) {
  10. throw new AssertionError("LoadedFrom cannot be null.");
  11. }
  12. action.complete(result, from);
  13. if (loggingEnabled) {
  14. log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);
  15. }
  16. } else {
  17. action.error();
  18. if (loggingEnabled) {
  19. log(OWNER_MAIN, VERB_ERRORED, action.request.logId());
  20. }
  21. }
  22. }

然后调用的是ImageViewAction里面的action.complete(result, from)方法:

  1. @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
  2. if (result == null) {
  3. throw new AssertionError(
  4. String.format("Attempted to complete action with no result!\n%s", this));
  5. }
  6. ImageView target = this.target.get();
  7. if (target == null) {
  8. return;
  9. }
  10. Context context = picasso.context;
  11. boolean indicatorsEnabled = picasso.indicatorsEnabled;
  12. PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
  13. if (callback != null) {
  14. callback.onSuccess();
  15. }
  16. }

最后一步通过PicassoDrawable.setBitmap来设置,就算大功完成了。

附上部分流程图:

图片加载框架Picasso解析的更多相关文章

  1. Android 图片加载框架Picasso基本使用和源码完全解析(巨细无比)

    写在之前 原本打算是每周更新一篇博文,同时记录一周的生活状态,但是稍微工作忙一点就顾不上写博客了.悲催 还是说下最近的状况,最近两周一直在接公司申请的计费点, 沃商店,银贝壳,微信等等,然后就是不停的 ...

  2. Android图片加载框架Picasso最全使用教程4

    通过前几篇的学习,我们已经对Picasso的加载图片的用法有了很深的了解,接下来我们开始分析Picasso为我们提供的其他高级功能及内存分析,Let’sGo ! Picasso进行图片的旋转(Rota ...

  3. Android图片加载框架Picasso最全使用教程3

    前面我们对Picasso的用法有了一定得了解,下面就分析一下一些特殊情况下,Picasso的用法. 调用.noFade() Picasso的默认图片加载方式有一个淡入的效果,如果调用了noFade() ...

  4. Android图片加载框架Picasso最全使用教程2

    前言 前面我们已经介绍了Picasso的基本用法及如何将一张图片加载到ImageView中,下面我们就利用Picasso在ListView中加载图片;Let’s Go! 一个ListView的简单应用 ...

  5. Android图片加载框架Picasso最全使用教程1

    Picasso介绍 Picasso是Square公司开源的一个Android图形缓存库 A powerful image downloading and caching library for And ...

  6. 第一次源码分析: 图片加载框架Picasso源码分析

    使用: Picasso.with(this) .load("http://imgstore.cdn.sogou.com/app/a/100540002/467502.jpg") . ...

  7. 源码分析: 图片加载框架Picasso源码分析

    使用: Picasso.with(this) .load("http://imgstore.cdn.sogou.com/app/a/100540002/467502.jpg") . ...

  8. Android图片加载框架Picasso最全使用教程5

    在之前的四篇博客中,我们学习了所有的关于Picasso的主要方法,我们也对这个Picasso有了一个很深的认识,下面就主要对Picasso自身进行分析,这样的话,会让我们更了解Picasso的核心方法 ...

  9. Android图片加载框架之Picasso

    相信做Android开发的对Square公司一定不会陌生,大名鼎鼎的网络请求框架Retrofit就来源于它,今天学习的是该公司出品的图片加载框架Picasso. 项目地址 https://github ...

随机推荐

  1. 视频分享:过五关斩六将——我要做IT面霸!

    这是我在某网站分享的网络直播课程,现在博客园分享给大家! 本视频适合以下朋友观看: 1)在校计算机及相关专业学生,希望你了解应聘的要求后来改善你的学习: 2)正在求职或打算跳槽的人士: 3)HR或公司 ...

  2. JAVA中的枚举小结

    枚举 将一组有限集合创建为一种新的类型,集合里面的值可以作为程序组件使用: 枚举基本特性 以下代码是枚举的简单使用: 使用values方法返回enum实例的数组 使用ordinal方法返回每个enum ...

  3. Storm系列(二):使用Csharp创建你的第一个Storm拓扑(wordcount)

    WordCount在大数据领域就像学习一门语言时的hello world,得益于Storm的开源以及Storm.Net.Adapter,现在我们也可以像Java或Python一样,使用Csharp创建 ...

  4. Servlet/JSP-06 Session

    一. 概述 Session 指客户端(浏览器)与服务器端之间保持状态的解决方案,有时候也用来指这种解决方案的存储结构. 当服务器端程序要为客户端的请求创建一个 Session 时,会首先检查这个请求里 ...

  5. JVM探索之——内存管理(一)

    本系列的第一篇文章,预计本系列最后面会有两三个案例. Java与C.C++不一样Java不需要Coder进行手动内存管理,而这一切都交给JVM进行自动内存管理,这从某种程度上来说也减轻了我们Coder ...

  6. GacUI学习(二)

    GacUI学习(一)之高仿系统记事本(二) 转载请注明来源:http://www.cnblogs.com/lyfh/p/6107614.html 上篇<GacUI学习(一)之高仿系统记事本(一) ...

  7. Android getevent

    详细用法如下: 源码复制打印? Usage: getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] ...

  8. android xml 布局错误(黑掉了)Missing styles. Is the correct theme chosen for this layout?

    发现在调整页面的时候 ,老是报以下错误,导致无法静态显示ui效果. Missing styles. Is the correct theme chosen for this layout? Use t ...

  9. 利用apply和arguments复用方法

    首先,有个单例对象,它上面挂了很多静态工具方法.其中有一个是each,用来遍历数组或对象. var nativeForEach = [].forEach var nativeMap = [].map ...

  10. python可分组字典

    # -*- encoding: UTF-8 -*- from collections import defaultdict class News(object): def __init__(self, ...