图片加载框架Picasso解析

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()方法:

[html] view plain copy

  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)默认方法来获得单例对象:

[html] view plain copy

  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. }

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

[html] view plain copy

  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对象:

[html] view plain copy

  1. public RequestCreator load(Uri uri) {
  2. return new RequestCreator(this, uri, 0);
  3. }

[html] view plain copy

  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)举例:

[html] view plain copy

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

他实际调用的是:

[html] view plain copy

  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. }

[html] view plain copy

  1. checkMain();

首先检查是否是主线程

[html] view plain copy

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

检查目标view是否存在

[html] view plain copy

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

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

[html] view plain copy

  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里。

[html] view plain copy

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

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

[html] view plain copy

  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 接口:

[html] view plain copy

  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的目的。

[html] view plain copy

  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从缓存中读取:

[html] view plain copy

  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接口,最后调用的是:

[html] view plain copy

  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>:

[html] view plain copy

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

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

[html] view plain copy

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

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

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

[html] view plain copy

  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加入到分发器中:

[html] view plain copy

  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()方法:

[html] view plain copy

  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对象:

[html] view plain copy

  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()方法:

[html] view plain copy

  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类型的):

[html] view plain copy

  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()方法:

[html] view plain copy

  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下的某个目录的磁盘缓存:

[html] view plain copy

  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()方法的流程继续:

[html] view plain copy

  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应用上:

[html] view plain copy

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

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

[html] view plain copy

  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. }

[html] view plain copy

  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:

[html] view plain copy

  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)方法:

[html] view plain copy

  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来设置,就算大功完成了。

附上部分流程图:

时间: 02-16

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

Android中图片加载框架Glide解析2----从源码的角度理解Glide的执行流程

转载地址:http://blog.csdn.net/guolin_blog/article/details/53939176 在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),Glide的基本用法 . 在多数情况下,我们想要在界面上加载并展示一张图片只需要一行代码就能实现,如下所示: Glide.with(this).load(url).into(i

图片加载框架之Glide和Picasso

Glide介绍 Glide是一个加载图片的库,作者是bumptech,它是在泰国举行的google 开发者论坛上google为我们介绍的,这个库被广泛的运用在google的开源项目中. Glide是一个非常成熟的图片加载库,他可以从多个源加载图片,如:网路,本地,Uri等,更重要的是他内部封装了非常好的缓存机制并且在处理图片的时候能保持一个低的内存消耗. Picasso介绍(毕加索) picasso是Square公司开源的一个Android图形缓存库,地址http://square.github

主流图片加载框架ImageLoader、Glide、Picasso、Fresco性能分析---图片加载速度比较

图片加载这种实现繁琐,可复用性又极强的东西,自然是选择使用图片加载框架来快速实现. 像是Android-Universal-Image-Loader.Glide.Picasso.Fresco之类, 但是这时候的烦恼在于,这么多图片加载框架到底谁最实用? 有说Fresco,因为支持WebP,还是用了NDK来加载图片,减少JavaHeap的使用 有Picasso,简洁高效 有说Glide,Picasso升级,可以加载Gif,在Picasso基础上扩展了很多方法 ImageLoader 使用最广,因为

Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53939176 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每天都有文章更新. 在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),Glide的基本用法 . 在多数情况下,我们想要在界面上加载并展示一

Android中常见的图片加载框架

图片加载涉及到图片的缓存.图片的处理.图片的显示等.而随着市面上手机设备的硬件水平飞速发展,对图片的显示要求越来越高,稍微处理不好就会造成内存溢出等问题.很多软件厂家的通用做法就是借用第三方的框架进行图片加载. 开源框架的源码还是挺复杂的,但使用较为简单.大部分框架其实都差不多,配置稍微麻烦点,但是使用时一般只需要一行,显示方法一般会提供多个重载方法,支持不同需要.这样会减少很不必要的麻烦.同时,第三方框架的使用较为方便,这大大的减少了工作量.提高了开发效率.本文主要介绍四种常用的图片加载框架,

关于图片加载框架

接上篇,这篇开始对现在比较的流行的第三方图片加载框架做一个对比总结. 这篇文章介绍内容如下: 1.目前流行的图片加载框架有什么? 2.各自的使用方法 3.各自的优缺点 4.优化问题 一.目前流行的图片加载框架有什么? ImageLoader   Glide  Picasso  Fresso(2015年) 注:由于现在ImageLoader使用较少,本篇博文将不再对它进行阐述.主要以其它三个框架为主,有兴趣的同学可以自行学习. 二.各自的使用方法 Picasso:  Picasso .with(t

Android 框架练成 教你打造高效的图片加载框架

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41874561,本文出自:[张鸿洋的博客] 1.概述 优秀的图片加载框架不要太多,什么UIL , Volley ,Picasso,Imageloader等等.但是作为一名合格的程序猿,必须懂其中的实现原理,于是乎,今天我就带大家一起来设计一个加载网络.本地的图片框架.有人可能会说,自己写会不会很渣,运行效率,内存溢出神马的.放心,我们拿demo说话,拼得就是速度,奏事这么任性.

常用图片加载框架

我们在项目中使用的是Picasso.其实现在的主流图片加载框架除了Picasso还有ImageLoader,Glide,以及Fresco.ImageLoader是比较老的框架了,稳定, 加载速度适中, 缺点在于不支持GIF图片加载, 使用稍微繁琐, 并且缓存机制没有和http的缓存很好的结合, 完全是自己的一套缓存机制.Glide是谷歌的一位工程师开发的,它可以说是Picasso的升级版, 有Picasso的优点, 并且支持GIF图片加载显示, 图片缓存也会自动缩放, 默认使用RGB_565格式

Universal-Image-Loader(UIL)图片加载框架使用简单介绍

这个也是最近项目中使用到的第三方图片加载框架,在这里也自己总结一下,简单的介绍一些使用的方式. UIL图片加载框架特点 简介: 项目地址:https://github.com/nostra13/Android-Universal-Image-Loader 异步加载图片或者加载大量图片经常会遇到图片错乱或者OOM等相关问题.UIL图片缓存,目前使用最广泛的图片缓存,支持主流图片缓存的绝大多数特性. 我们看下该图片加载的三级缓存原理 特点: 1.多线程下载图片,图片可以来源于网络,文件系统,项目文件