Picasso源码原理分析

作者:谭东

QQ:852041173


Picasso是美国SQUARE移动支付公司开源的图片缓存加载库。可以实现图片下载和缓存功能,效率和性能都很不错。


Square公司官方博客:http://square.github.io/

Square公司Github地址:https://github.com/square

Square公司Picasso的Wiki地址:http://square.github.io/picasso/

Square公司Picasso的Github地址:https://github.com/square/picasso


Picasso源码原理解析:

先看流程图:



Picasso的源码很小,类也很少,相对简单些。

我们主要关注以下标红的几个类。


不是太想写太多的文字,那就看源码分析吧。

先看构造单例模式的Piasso实例:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.          * with方式 
  3.          */  
  4.         Picasso.with(this).load("http://square.github.io/picasso/static/sample.png").centerCrop().into(imageView);  
  5.         /** 
  6.          * new with方式 
  7.          */  
  8.         Picasso picasso = Picasso.with(this);  
  9.         /** 
  10.          * builder方式 
  11.          */  
  12.         picasso = new Picasso.Builder(this).executor(threadPoolExecutor).downloader(new OkHttp3Downloader(okHttpClient)).build();//默认3个线程  
  13.         Picasso.setSingletonInstance(picasso);  

这几种方式通过查看,都是通过一个方式实现的构造,那就是builder方式。

我们按照这句最基本的方式进行源码分析。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Picasso.with(this).load("http://square.github.io/picasso/static/sample.png").centerCrop().into(imageView);  

点击with方法进去。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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.   }  
  11.   
  12.   
  13.     /** Create the {@link Picasso} instance. */  
  14.     public Picasso build() {  
  15.       Context context = this.context;  
  16.       //是否自定义下载器,无的话使用默认下载器  
  17.       if (downloader == null) {  
  18.         downloader = Utils.createDefaultDownloader(context);  
  19.       }  
  20.       //是否自定义缓存方式,无的话使用默认LruCache  
  21.       if (cache == null) {  
  22.         cache = new LruCache(context);  
  23.       }  
  24.       //是否自定义线程池,无的话使用默认线程池  
  25.       if (service == null) {  
  26.         service = new PicassoExecutorService();  
  27.       }  
  28.       //是否自定义转换器,无的话使用默认转换器  
  29.       if (transformer == null) {  
  30.         transformer = RequestTransformer.IDENTITY;  
  31.       }  
  32.       //统计器,统计缓存的命中率  
  33.       Stats stats = new Stats(cache);  
  34.       //创建Dispather,分发调度器  
  35.       Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);  
  36.       //创建Picasso实例  
  37.       return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,  
  38.           defaultBitmapConfig, indicatorsEnabled, loggingEnabled);  
  39.     }  
  40.   }  

可以看出都是通过builder方式实例化的Picasso类。这里实例化了Dispatcher和Picasso。

接下来点击load方法进去:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.   * Start an image request using the specified path. This is a convenience method for calling 
  3.   * {@link #load(Uri)}. 
  4.   * <p> 
  5.   * This path may be a remote URL, file resource (prefixed with {@code file:}), content resource 
  6.   * (prefixed with {@code content:}), or android resource (prefixed with {@code 
  7.   * android.resource:}. 
  8.   * <p> 
  9.   * Passing {@code null} as a {@code path} will not trigger any request but will set a 
  10.   * placeholder, if one is specified. 
  11.   * 
  12.   * @see #load(Uri) 
  13.   * @see #load(File) 
  14.   * @see #load(int) 
  15.   * @throws IllegalArgumentException if {@code path} is empty or blank string. 
  16.   */  
  17.  public RequestCreator load(String path) {  
  18.    if (path == null) {  
  19.      return new RequestCreator(thisnull0);  
  20.    }  
  21.    if (path.trim().length() == 0) {  
  22.      throw new IllegalArgumentException("Path must not be empty.");  
  23.    }  
  24.    return load(Uri.parse(path));  
  25.  }  
  26.   
  27. /** 
  28.   * Start an image request using the specified URI. 
  29.   * <p> 
  30.   * Passing {@code null} as a {@code uri} will not trigger any request but will set a placeholder, 
  31.   * if one is specified. 
  32.   * 
  33.   * @see #load(File) 
  34.   * @see #load(String) 
  35.   * @see #load(int) 
  36.   */  
  37.  public RequestCreator load(Uri uri) {  
  38.    return new RequestCreator(this, uri, 0);  
  39.  }  
  40.   
  41.  RequestCreator(Picasso picasso, Uri uri, int resourceId) {  
  42.    if (picasso.shutdown) {  
  43.      throw new IllegalStateException(  
  44.          "Picasso instance already shut down. Cannot submit new requests.");  
  45.    }  
  46.    this.picasso = picasso;  
  47.    this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);  
  48.  }  
可以看出,这里实例化了一个RequestCreator,我们看下这个源码类干嘛的。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** Fluent API for building an image download request. */  
  2. @SuppressWarnings("UnusedDeclaration"// Public API.  
  3. public class RequestCreator {  
  4.   private static final AtomicInteger nextId = new AtomicInteger();  
  5.   
  6.   private final Picasso picasso;  
  7.   private final Request.Builder data;  
  8.   
  9.   private boolean noFade;  
  10.   private boolean deferred;  
  11.   private boolean setPlaceholder = true;  
  12.   private int placeholderResId;  
  13.   private int errorResId;  
  14.   private int memoryPolicy;  
  15.   private int networkPolicy;  
  16.   private Drawable placeholderDrawable;  
  17.   private Drawable errorDrawable;  
  18.   private Object tag;  
看下基本的声明,就是进行我们个性化配置存储的类。

我们接下来点击centerCrop方法进去:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Crops an image inside of the bounds specified by {@link #resize(int, int)} rather than 
  3.  * distorting the aspect ratio. This cropping technique scales the image so that it fills the 
  4.  * requested bounds and then crops the extra. 
  5.  */  
  6. public RequestCreator centerCrop() {  
  7.   data.centerCrop();  
  8.   return this;  
  9. }  

这个配置也是操作我们的RequestCreator。

最后点击into方法。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Asynchronously fulfills the request into the specified {@link ImageView}. 
  3.  * <p> 
  4.  * <em>Note:</em> This method keeps a weak reference to the {@link ImageView} instance and will 
  5.  * automatically support object recycling. 
  6.  */  
  7. public void into(ImageView target) {  
  8.   into(target, null);  
  9. }  
  10. **  
  11.  * Asynchronously fulfills the request into the specified {@link ImageView} and invokes the  
  12.  * target {@link Callback} if it's not {@code null}.  
  13.  * <p>  
  14.  * <em>Note:</em> The {@link Callback} param is a strong reference and will prevent your  
  15.  * {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected. If  
  16.  * you use this method, it is <b>strongly</b> recommended you invoke an adjacent  
  17.  * {@link Picasso#cancelRequest(android.widget.ImageView)} call to prevent temporary leaking.  
  18.  */  
  19. public void into(ImageView target, Callback callback) {  
  20.   long started = System.nanoTime();  
  21.   checkMain();  
  22.   
  23.   if (target == null) {  
  24.     throw new IllegalArgumentException("Target must not be null.");  
  25.   }  
  26.   
  27.   if (!data.hasImage()) {//清除取消存在请求  
  28.     picasso.cancelRequest(target);  
  29.     if (setPlaceholder) {  
  30.       setPlaceholder(target, getPlaceholderDrawable());  
  31.     }  
  32.     return;  
  33.   }  
  34.   
  35.   if (deferred) {  
  36.     if (data.hasSize()) {  
  37.       throw new IllegalStateException("Fit cannot be used with resize.");  
  38.     }  
  39.     int width = target.getWidth();  
  40.     int height = target.getHeight();  
  41.     if (width == 0 || height == 0) {  
  42.       if (setPlaceholder) {  
  43.         setPlaceholder(target, getPlaceholderDrawable());  
  44.       }  
  45.       picasso.defer(target, new DeferredRequestCreator(this, target, callback));  
  46.       return;  
  47.     }  
  48.     data.resize(width, height);  
  49.   }  
  50.   
  51.   Request request = createRequest(started);//时间戳为Key,创建Request  
  52.   String requestKey = createKey(request);  
  53.   
  54.   if (shouldReadFromMemoryCache(memoryPolicy)) {//内存缓存检查  
  55.     Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);  
  56.     if (bitmap != null) {  
  57.       picasso.cancelRequest(target);  
  58.       setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);  
  59.       if (picasso.loggingEnabled) {  
  60.         log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);  
  61.       }  
  62.       if (callback != null) {  
  63.         callback.onSuccess();  
  64.       }  
  65.       return;  
  66.     }  
  67.   }  
  68.   
  69.   if (setPlaceholder) {  
  70.     setPlaceholder(target, getPlaceholderDrawable());  
  71.   }  
  72.   //关键,创建Action,这里用的ImageViewAction  
  73.   Action action =  
  74.       new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,  
  75.           errorDrawable, requestKey, tag, callback, noFade);  
  76.   //把请求封装成Action,提交分发  
  77.   picasso.enqueueAndSubmit(action);  
  78. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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) {//提交给Dispatcher分发  
  11.    dispatcher.dispatchSubmit(action);  
  12.  }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void dispatchSubmit(Action action) {  
  2.   handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));  
  3. }  
  4. private static class DispatcherHandler extends Handler {  
  5.   private final Dispatcher dispatcher;  
  6.   
  7.   public DispatcherHandler(Looper looper, Dispatcher dispatcher) {  
  8.     super(looper);  
  9.     this.dispatcher = dispatcher;  
  10.   }  
  11.   
  12.   @Override public void handleMessage(final Message msg) {  
  13.     switch (msg.what) {  
  14.       case REQUEST_SUBMIT: {//提交分发下载请求  
  15.         Action action = (Action) msg.obj;  
  16.         dispatcher.performSubmit(action);  
  17.         break;  
  18.       }  
  19.       case REQUEST_CANCEL: {  
  20.         Action action = (Action) msg.obj;  
  21.         dispatcher.performCancel(action);  
  22.         break;  
  23.       }  
  24.       case TAG_PAUSE: {  
  25.         Object tag = msg.obj;  
  26.         dispatcher.performPauseTag(tag);  
  27.         break;  
  28.       }  
  29.       case TAG_RESUME: {  
  30.         Object tag = msg.obj;  
  31.         dispatcher.performResumeTag(tag);  
  32.         break;  
  33.       }  
  34.       case HUNTER_COMPLETE: {  
  35.         BitmapHunter hunter = (BitmapHunter) msg.obj;  
  36.         dispatcher.performComplete(hunter);  
  37.         break;  
  38.       }  
  39.       case HUNTER_RETRY: {  
  40.         BitmapHunter hunter = (BitmapHunter) msg.obj;  
  41.         dispatcher.performRetry(hunter);  
  42.         break;  
  43.       }  
  44.       case HUNTER_DECODE_FAILED: {  
  45.         BitmapHunter hunter = (BitmapHunter) msg.obj;  
  46.         dispatcher.performError(hunter, false);  
  47.         break;  
  48.       }  
  49.       case HUNTER_DELAY_NEXT_BATCH: {  
  50.         dispatcher.performBatchComplete();  
  51.         break;  
  52.       }  
  53.       case NETWORK_STATE_CHANGE: {  
  54.         NetworkInfo info = (NetworkInfo) msg.obj;  
  55.         dispatcher.performNetworkStateChange(info);  
  56.         break;  
  57.       }  
  58.       case AIRPLANE_MODE_CHANGE: {  
  59.         dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON);  
  60.         break;  
  61.       }  
  62.       default:  
  63.         Picasso.HANDLER.post(new Runnable() {  
  64.           @Override public void run() {  
  65.             throw new AssertionError("Unknown handler message received: " + msg.what);  
  66.           }  
  67.         });  
  68.     }  
  69.   }  
  70. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.   void performSubmit(Action action) {  
  2.     performSubmit(action, true);  
  3.   }  
  4.   
  5.   void performSubmit(Action action, boolean dismissFailed) {  
  6.     if (pausedTags.contains(action.getTag())) {  
  7.       pausedActions.put(action.getTarget(), action);  
  8.       if (action.getPicasso().loggingEnabled) {  
  9.         log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),  
  10.             "because tag '" + action.getTag() + "' is paused");  
  11.       }  
  12.       return;  
  13.     }  
  14.    //BitmapHunter,核心,它是个线程,执行图片下载编解码  
  15.     BitmapHunter hunter = hunterMap.get(action.getKey());  
  16.     if (hunter != null) {  
  17.       hunter.attach(action);  
  18.       return;  
  19.     }  
  20.   
  21.     if (service.isShutdown()) {  
  22.       if (action.getPicasso().loggingEnabled) {  
  23.         log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");  
  24.       }  
  25.       return;  
  26.     }  
  27.   
  28.     hunter = forRequest(action.getPicasso(), this, cache, stats, action);  
  29.     hunter.future = service.submit(hunter);//这个service是ExecutorService线程池,提交执行下载线程  
  30.     hunterMap.put(action.getKey(), hunter);  
  31.     if (dismissFailed) {  
  32.       failedActions.remove(action.getTarget());  
  33.     }  
  34.   
  35.     if (action.getPicasso().loggingEnabled) {  
  36.       log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());  
  37.     }  
  38.   }  

大概看下BitmapHunter源码:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * Copyright (C) 2013 Square, Inc. 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16. package com.squareup.picasso;  
  17.   
  18. import android.graphics.Bitmap;  
  19. import android.graphics.BitmapFactory;  
  20. import android.graphics.Matrix;  
  21. import android.net.NetworkInfo;  
  22. import java.io.IOException;  
  23. import java.io.InputStream;  
  24. import java.io.PrintWriter;  
  25. import java.io.StringWriter;  
  26. import java.util.ArrayList;  
  27. import java.util.List;  
  28. import java.util.concurrent.Future;  
  29. import java.util.concurrent.atomic.AtomicInteger;  
  30.   
  31. import static com.squareup.picasso.MemoryPolicy.shouldReadFromMemoryCache;  
  32. import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;  
  33. import static com.squareup.picasso.Picasso.Priority;  
  34. import static com.squareup.picasso.Picasso.Priority.LOW;  
  35. import static com.squareup.picasso.Utils.OWNER_HUNTER;  
  36. import static com.squareup.picasso.Utils.VERB_DECODED;  
  37. import static com.squareup.picasso.Utils.VERB_EXECUTING;  
  38. import static com.squareup.picasso.Utils.VERB_JOINED;  
  39. import static com.squareup.picasso.Utils.VERB_REMOVED;  
  40. import static com.squareup.picasso.Utils.VERB_TRANSFORMED;  
  41. import static com.squareup.picasso.Utils.getLogIdsForHunter;  
  42. import static com.squareup.picasso.Utils.log;  
  43.   
  44. class BitmapHunter implements Runnable {  
  45.   /** 
  46.    * Global lock for bitmap decoding to ensure that we are only are decoding one at a time. Since 
  47.    * this will only ever happen in background threads we help avoid excessive memory thrashing as 
  48.    * well as potential OOMs. Shamelessly stolen from Volley. 
  49.    */  
  50.   private static final Object DECODE_LOCK = new Object();  
  51.   
  52.   private static final ThreadLocal<StringBuilder> NAME_BUILDER = new ThreadLocal<StringBuilder>() {  
  53.     @Override protected StringBuilder initialValue() {  
  54.       return new StringBuilder(Utils.THREAD_PREFIX);  
  55.     }  
  56.   };  
  57.   
  58.   private static final AtomicInteger SEQUENCE_GENERATOR = new AtomicInteger();  
  59.   
  60.   private static final RequestHandler ERRORING_HANDLER = new RequestHandler() {  
  61.     @Override public boolean canHandleRequest(Request data) {  
  62.       return true;  
  63.     }  
  64.   
  65.     @Override public Result load(Request request, int networkPolicy) throws IOException {  
  66.       throw new IllegalStateException("Unrecognized type of request: " + request);  
  67.     }  
  68.   };  
  69.   
  70.   final int sequence;  
  71.   final Picasso picasso;  
  72.   final Dispatcher dispatcher;  
  73.   final Cache cache;  
  74.   final Stats stats;  
  75.   final String key;  
  76.   final Request data;  
  77.   final int memoryPolicy;  
  78.   int networkPolicy;  
  79.   final RequestHandler requestHandler;  
  80.   
  81.   Action action;  
  82.   List<Action> actions;  
  83.   Bitmap result;  
  84.   Future<?> future;  
  85.   Picasso.LoadedFrom loadedFrom;  
  86.   Exception exception;  
  87.   int exifRotation; // Determined during decoding of original resource.  
  88.   int retryCount;  
  89.   Priority priority;  
  90.   
  91.   BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action,  
  92.       RequestHandler requestHandler) {  
  93.     this.sequence = SEQUENCE_GENERATOR.incrementAndGet();  
  94.     this.picasso = picasso;  
  95.     this.dispatcher = dispatcher;  
  96.     this.cache = cache;  
  97.     this.stats = stats;  
  98.     this.action = action;  
  99.     this.key = action.getKey();  
  100.     this.data = action.getRequest();  
  101.     this.priority = action.getPriority();  
  102.     this.memoryPolicy = action.getMemoryPolicy();  
  103.     this.networkPolicy = action.getNetworkPolicy();  
  104.     this.requestHandler = requestHandler;  
  105.     this.retryCount = requestHandler.getRetryCount();  
  106.   }  
它是实现了Runable接口的线程,再继续看Run方法。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override public void run() {  
  2.   try {  
  3.     updateThreadName(data);  
  4.   
  5.     if (picasso.loggingEnabled) {  
  6.       log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));  
  7.     }  
  8.     //这个result是Bitmap,调用了hunt方法获取  
  9.     result = hunt();  
  10.   
  11.     if (result == null) {//分发图片加载失败  
  12.       dispatcher.dispatchFailed(this);  
  13.     } else {//分发图片加载完成  
  14.       dispatcher.dispatchComplete(this);  
  15.     }  
  16.   } catch (Downloader.ResponseException e) {  
  17.     if (!e.localCacheOnly || e.responseCode != 504) {  
  18.       exception = e;  
  19.     }  
  20.     dispatcher.dispatchFailed(this);  
  21.   } catch (NetworkRequestHandler.ContentLengthException e) {  
  22.     exception = e;  
  23.     dispatcher.dispatchRetry(this);  
  24.   } catch (IOException e) {  
  25.     exception = e;  
  26.     dispatcher.dispatchRetry(this);  
  27.   } catch (OutOfMemoryError e) {  
  28.     StringWriter writer = new StringWriter();  
  29.     stats.createSnapshot().dump(new PrintWriter(writer));  
  30.     exception = new RuntimeException(writer.toString(), e);  
  31.     dispatcher.dispatchFailed(this);  
  32.   } catch (Exception e) {  
  33.     exception = e;  
  34.     dispatcher.dispatchFailed(this);  
  35.   } finally {  
  36.     Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);  
  37.   }  
  38. }  
再继续看hunt方法:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Bitmap hunt() throws IOException {  
  2.    Bitmap bitmap = null;  
  3.    //内存缓存检查,检查了很多次内存缓存了  
  4.    if (shouldReadFromMemoryCache(memoryPolicy)) {  
  5.      bitmap = cache.get(key);  
  6.      if (bitmap != null) {  
  7.        stats.dispatchCacheHit();  
  8.        loadedFrom = MEMORY;  
  9.        if (picasso.loggingEnabled) {  
  10.          log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");  
  11.        }  
  12.        return bitmap;  
  13.      }  
  14.    }  
  15.   
  16.    data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;  
  17.    RequestHandler.Result result = requestHandler.load(data, networkPolicy);  
  18.    if (result != null) {  
  19.      loadedFrom = result.getLoadedFrom();  
  20.      exifRotation = result.getExifOrientation();  
  21.   
  22.      bitmap = result.getBitmap();  
  23.   
  24.      // If there was no Bitmap then we need to decode it from the stream.  
  25.      if (bitmap == null) {  
  26.        InputStream is = result.getStream();  
  27.        try {//inputstream解码  
  28.          bitmap = decodeStream(is, data);  
  29.        } finally {  
  30.          Utils.closeQuietly(is);  
  31.        }  
  32.      }  
  33.    }  
  34.   
  35.    if (bitmap != null) {  
  36.      if (picasso.loggingEnabled) {  
  37.        log(OWNER_HUNTER, VERB_DECODED, data.logId());  
  38.      }  
  39.      stats.dispatchBitmapDecoded(bitmap);//解码命中率统计  
  40.      if (data.needsTransformation() || exifRotation != 0) {  
  41.        synchronized (DECODE_LOCK) {  
  42.          if (data.needsMatrixTransform() || exifRotation != 0) {  
  43.            bitmap = transformResult(data, bitmap, exifRotation);  
  44.            if (picasso.loggingEnabled) {  
  45.              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());  
  46.            }  
  47.          }  
  48.          if (data.hasCustomTransformations()) {  
  49.            bitmap = applyCustomTransformations(data.transformations, bitmap);  
  50.            if (picasso.loggingEnabled) {  
  51.              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");  
  52.            }  
  53.          }  
  54.        }  
  55.        if (bitmap != null) {  
  56.          stats.dispatchBitmapTransformed(bitmap);  
  57.        }  
  58.      }  
  59.    }  
  60.   
  61.    return bitmap;  
  62.  }  
看decodeStream方法里的逻辑:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Decode a byte stream into a Bitmap. This method will take into account additional information 
  3.  * about the supplied request in order to do the decoding efficiently (such as through leveraging 
  4.  * {@code inSampleSize}). 
  5.  */  
  6. static Bitmap decodeStream(InputStream stream, Request request) throws IOException {  
  7.   MarkableInputStream markStream = new MarkableInputStream(stream);  
  8.   stream = markStream;  
  9.   
  10.   long mark = markStream.savePosition(65536); // TODO fix this crap.  
  11.   
  12.   final BitmapFactory.Options options = RequestHandler.createBitmapOptions(request);  
  13.   final boolean calculateSize = RequestHandler.requiresInSampleSize(options);  
  14.   
  15.   boolean isWebPFile = Utils.isWebPFile(stream);  
  16.   markStream.reset(mark);  
  17.   // When decode WebP network stream, BitmapFactory throw JNI Exception and make app crash.  
  18.   // Decode byte array instead  
  19.   if (isWebPFile) {  
  20.     byte[] bytes = Utils.toByteArray(stream);  
  21.     if (calculateSize) {  
  22.       BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);  
  23.       RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,  
  24.           request);  
  25.     }  
  26.     return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);  
  27.   } else {  
  28.     if (calculateSize) {  
  29.       BitmapFactory.decodeStream(stream, null, options);  
  30.       RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,  
  31.           request);  
  32.   
  33.       markStream.reset(mark);  
  34.     }  
  35.     Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);  
  36.     if (bitmap == null) {  
  37.       // Treat null as an IO exception, we will eventually retry.  
  38.       throw new IOException("Failed to decode stream.");  
  39.     }  
  40.     return bitmap;  
  41.   }  
  42. }  
就是对inputSteam转为Bimap操作。

接下来,看dispatcher派发加载成功消息的逻辑:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void dispatchComplete(BitmapHunter hunter) {  
  2.   handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));  
  3. }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. case HUNTER_COMPLETE: {//加载完成  
  2.          BitmapHunter hunter = (BitmapHunter) msg.obj;  
  3.          dispatcher.performComplete(hunter);  
  4.          break;  
  5.        }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void performComplete(BitmapHunter hunter) {  
  2.   if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {  
  3.     cache.set(hunter.getKey(), hunter.getResult());  
  4.   }  
  5.   hunterMap.remove(hunter.getKey());  
  6.   batch(hunter);  
  7.   if (hunter.getPicasso().loggingEnabled) {  
  8.     log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");  
  9.   }  
  10. }  
  11. private void batch(BitmapHunter hunter) {  
  12.   if (hunter.isCancelled()) {  
  13.     return;  
  14.   }  
  15.   batch.add(hunter);  
  16.   if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {  
  17.     handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);  
  18.   }  
  19. }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. case HUNTER_DELAY_NEXT_BATCH: {//继续派发next_batch消息  
  2.          dispatcher.performBatchComplete();  
  3.          break;  
  4.        }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void performBatchComplete() {  
  2.    List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);  
  3.    batch.clear();  
  4.    mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));//注意这里的mainThreadHandler是Picasso的Handler  
  5.    logBatch(copy);  
  6.  }  

注意这里的mainThreadHander是Picasso里的Handler,我们可以看下Dispatcher的实例化。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,  
  2.      Downloader downloader, Cache cache, Stats stats) {  
  3.    this.dispatcherThread = new DispatcherThread();  
  4.    this.dispatcherThread.start();  
  5.    Utils.flushStackLocalLeaks(dispatcherThread.getLooper());  
  6.    this.context = context;  
  7.    this.service = service;  
  8.    this.hunterMap = new LinkedHashMap<String, BitmapHunter>();  
  9.    this.failedActions = new WeakHashMap<Object, Action>();  
  10.    this.pausedActions = new WeakHashMap<Object, Action>();  
  11.    this.pausedTags = new HashSet<Object>();  
  12.    this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);  
  13.    this.downloader = downloader;  
  14.    this.mainThreadHandler = mainThreadHandler;//这个就是Picasso传进来的mainThreadHandler  
  15.    this.cache = cache;  
  16.    this.stats = stats;  
  17.    this.batch = new ArrayList<BitmapHunter>(4);  
  18.    this.airplaneMode = Utils.isAirplaneModeOn(this.context);  
  19.    this.scansNetworkChanges = hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);  
  20.    this.receiver = new NetworkBroadcastReceiver(this);  
  21.    receiver.register();  
  22.  }  

那我们看下Picasso里的HUNTER_BATCH_COMPLETE消息处理:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static final String TAG = "Picasso";  
  2.  static final Handler HANDLER = new Handler(Looper.getMainLooper()) {  
  3.    @Override public void handleMessage(Message msg) {  
  4.      switch (msg.what) {  
  5.        case HUNTER_BATCH_COMPLETE: {  
  6.          @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;  
  7.          //noinspection ForLoopReplaceableByForEach  
  8.          for (int i = 0, n = batch.size(); i < n; i++) {  
  9.            BitmapHunter hunter = batch.get(i);  
  10.            hunter.picasso.complete(hunter);//回调了complete方法  
  11.          }  
  12.          break;  
  13.        }  
  14.        case REQUEST_GCED: {  
  15.          Action action = (Action) msg.obj;  
  16.          if (action.getPicasso().loggingEnabled) {  
  17.            log(OWNER_MAIN, VERB_CANCELED, action.request.logId(), "target got garbage collected");  
  18.          }  
  19.          action.picasso.cancelExistingRequest(action.getTarget());  
  20.          break;  
  21.        }  
  22.        case REQUEST_BATCH_RESUME:  
  23.          @SuppressWarnings("unchecked") List<Action> batch = (List<Action>) msg.obj;  
  24.          //noinspection ForLoopReplaceableByForEach  
  25.          for (int i = 0, n = batch.size(); i < n; i++) {  
  26.            Action action = batch.get(i);  
  27.            action.picasso.resumeAction(action);  
  28.          }  
  29.          break;  
  30.        default:  
  31.          throw new AssertionError("Unknown handler message received: " + msg.what);  
  32.      }  
  33.    }  
  34.  };  

我们看下Picasso的complete方法:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void complete(BitmapHunter hunter) {  
  2.    Action single = hunter.getAction();//拿到了派发过来的Action  
  3.    List<Action> joined = hunter.getActions();  
  4.   
  5.    boolean hasMultiple = joined != null && !joined.isEmpty();  
  6.    boolean shouldDeliver = single != null || hasMultiple;  
  7.   
  8.    if (!shouldDeliver) {  
  9.      return;  
  10.    }  
  11.   
  12.    Uri uri = hunter.getData().uri;  
  13.    Exception exception = hunter.getException();  
  14.    Bitmap result = hunter.getResult();//拿到了Bitmap  
  15.    LoadedFrom from = hunter.getLoadedFrom();  
  16.   
  17.    if (single != null) {  
  18.      deliverAction(result, from, single);//执行deliverAction方法  
  19.    }  
  20.   
  21.    if (hasMultiple) {  
  22.      //noinspection ForLoopReplaceableByForEach  
  23.      for (int i = 0, n = joined.size(); i < n; i++) {  
  24.        Action join = joined.get(i);  
  25.        deliverAction(result, from, join);  
  26.      }  
  27.    }  
  28.   
  29.    if (listener != null && exception != null) {  
  30.      listener.onImageLoadFailed(this, uri, exception);  
  31.    }  
  32.  }  
我们看下deliverAction方法:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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);//回调了action的complete方法  
  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.  }  
这里注意下,我们之前用的是Action里的ImageViewAction类,我们看下ImageViewAction源码和它的complete类里的处理逻辑:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * Copyright (C) 2013 Square, Inc. 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16. package com.squareup.picasso;  
  17.   
  18. import android.content.Context;  
  19. import android.graphics.Bitmap;  
  20. import android.graphics.drawable.Drawable;  
  21. import android.widget.ImageView;  
  22.   
  23. class ImageViewAction extends Action<ImageView> {  
  24.   
  25.   Callback callback;  
  26.   
  27.   ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,  
  28.       int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,  
  29.       Callback callback, boolean noFade) {  
  30.     super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,  
  31.         tag, noFade);  
  32.     this.callback = callback;  
  33.   }  
  34.   //图片加载完成回调  
  35.   @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {  
  36.     if (result == null) {  
  37.       throw new AssertionError(  
  38.           String.format("Attempted to complete action with no result!\n%s"this));  
  39.     }  
  40.   
  41.     ImageView target = this.target.get();  
  42.     if (target == null) {  
  43.       return;  
  44.     }  
  45.   
  46.     Context context = picasso.context;  
  47.     boolean indicatorsEnabled = picasso.indicatorsEnabled;  
  48.     PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);//设置bitmap给PicassoDrawable  
  49.   
  50.     if (callback != null) {  
  51.       callback.onSuccess();  
  52.     }  
  53.   }  
  54.   
  55.   @Override public void error() {  
  56.     ImageView target = this.target.get();  
  57.     if (target == null) {  
  58.       return;  
  59.     }  
  60.     if (errorResId != 0) {  
  61.       target.setImageResource(errorResId);  
  62.     } else if (errorDrawable != null) {  
  63.       target.setImageDrawable(errorDrawable);  
  64.     }  
  65.   
  66.     if (callback != null) {  
  67.       callback.onError();  
  68.     }  
  69.   }  
  70.   
  71.   @Override void cancel() {  
  72.     super.cancel();  
  73.     if (callback != null) {  
  74.       callback = null;  
  75.     }  
  76.   }  
  77. }  
我们简单看下PicassoDrawable源码和它的setBitmap方法:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * Copyright (C) 2013 Square, Inc. 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16. package com.squareup.picasso;  
  17.   
  18. import android.content.Context;  
  19. import android.graphics.Bitmap;  
  20. import android.graphics.Canvas;  
  21. import android.graphics.ColorFilter;  
  22. import android.graphics.Paint;  
  23. import android.graphics.Path;  
  24. import android.graphics.Point;  
  25. import android.graphics.Rect;  
  26. import android.graphics.drawable.AnimationDrawable;  
  27. import android.graphics.drawable.BitmapDrawable;  
  28. import android.graphics.drawable.Drawable;  
  29. import android.os.Build;  
  30. import android.os.SystemClock;  
  31. import android.widget.ImageView;  
  32.   
  33. import static android.graphics.Color.WHITE;  
  34. import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;  
  35.   
  36. final class PicassoDrawable extends BitmapDrawable {//继承自BitmapDrawable  
  37.   // Only accessed from main thread.  
  38.   private static final Paint DEBUG_PAINT = new Paint();  
  39.   private static final float FADE_DURATION = 200f; //ms  
  40.   
  41.   /** 
  42.    * Create or update the drawable on the target {@link ImageView} to display the supplied bitmap 
  43.    * image. 
  44.    */  
  45.   static void setBitmap(ImageView target, Context context, Bitmap bitmap,  
  46.       Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {  
  47.     Drawable placeholder = target.getDrawable();  
  48.     if (placeholder instanceof AnimationDrawable) {  
  49.       ((AnimationDrawable) placeholder).stop();  
  50.     }  
  51.     PicassoDrawable drawable =  
  52.         new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);  
  53.     target.setImageDrawable(drawable);//调用ImageView的setImageDrawable方法将转换后的drawable加载进去显示  
  54.   }  
  55.   
  56.   /** 
  57.    * Create or update the drawable on the target {@link ImageView} to display the supplied 
  58.    * placeholder image. 
  59.    */  
  60.   static void setPlaceholder(ImageView target, Drawable placeholderDrawable) {  
  61.     target.setImageDrawable(placeholderDrawable);  
  62.     if (target.getDrawable() instanceof AnimationDrawable) {  
  63.       ((AnimationDrawable) target.getDrawable()).start();  
  64.     }  
  65.   }  
  66.   
  67.   private final boolean debugging;  
  68.   private final float density;  
  69.   private final Picasso.LoadedFrom loadedFrom;  
  70.   
  71.   Drawable placeholder;  
  72.   
  73.   long startTimeMillis;  
  74.   boolean animating;  
  75.   int alpha = 0xFF;  
  76.   
  77.   PicassoDrawable(Context context, Bitmap bitmap, Drawable placeholder,  
  78.       Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {  
  79.     super(context.getResources(), bitmap);  
  80.   
  81.     this.debugging = debugging;  
  82.     this.density = context.getResources().getDisplayMetrics().density;  
  83.   
  84.     this.loadedFrom = loadedFrom;  
  85.   
  86.     boolean fade = loadedFrom != MEMORY && !noFade;  
  87.     if (fade) {  
  88.       this.placeholder = placeholder;  
  89.       animating = true;  
  90.       startTimeMillis = SystemClock.uptimeMillis();  
  91.     }  
  92.   }  
  93.   
  94.   @Override public void draw(Canvas canvas) {  
  95.     if (!animating) {  
  96.       super.draw(canvas);  
  97.     } else {  
  98.       float normalized = (SystemClock.uptimeMillis() - startTimeMillis) / FADE_DURATION;  
  99.       if (normalized >= 1f) {  
  100.         animating = false;  
  101.         placeholder = null;  
  102.         super.draw(canvas);  
  103.       } else {  
  104.         if (placeholder != null) {  
  105.           placeholder.draw(canvas);  
  106.         }  
  107.   
  108.         int partialAlpha = (int) (alpha * normalized);  
  109.         super.setAlpha(partialAlpha);  
  110.         super.draw(canvas);  
  111.         super.setAlpha(alpha);  
  112.         if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {  
  113.           invalidateSelf();  
  114.         }  
  115.       }  
  116.     }  
  117.   
  118.     if (debugging) {  
  119.       drawDebugIndicator(canvas);  
  120.     }  
  121.   }  
  122.   
  123.   @Override public void setAlpha(int alpha) {  
  124.     this.alpha = alpha;  
  125.     if (placeholder != null) {  
  126.       placeholder.setAlpha(alpha);  
  127.     }  
  128.     super.setAlpha(alpha);  
  129.   }  
  130.   
  131.   @Override public void setColorFilter(ColorFilter cf) {  
  132.     if (placeholder != null) {  
  133.       placeholder.setColorFilter(cf);  
  134.     }  
  135.     super.setColorFilter(cf);  
  136.   }  
  137.   
  138.   @Override protected void onBoundsChange(Rect bounds) {  
  139.     if (placeholder != null) {  
  140.       placeholder.setBounds(bounds);  
  141.     }  
  142.     super.onBoundsChange(bounds);  
  143.   }  
  144.   
  145.   private void drawDebugIndicator(Canvas canvas) {  
  146.     DEBUG_PAINT.setColor(WHITE);  
  147.     Path path = getTrianglePath(new Point(00), (int) (16 * density));  
  148.     canvas.drawPath(path, DEBUG_PAINT);  
  149.   
  150.     DEBUG_PAINT.setColor(loadedFrom.debugColor);  
  151.     path = getTrianglePath(new Point(00), (int) (15 * density));  
  152.     canvas.drawPath(path, DEBUG_PAINT);  
  153.   }  
  154.   
  155.   private static Path getTrianglePath(Point p1, int width) {  
  156.     Point p2 = new Point(p1.x + width, p1.y);  
  157.     Point p3 = new Point(p1.x, p1.y + width);  
  158.   
  159.     Path path = new Path();  
  160.     path.moveTo(p1.x, p1.y);  
  161.     path.lineTo(p2.x, p2.y);  
  162.     path.lineTo(p3.x, p3.y);  
  163.   
  164.     return path;  
  165.   }  
  166. }  
这样,整个Picasso就实现了我们的图片请求加载显示。

Picasso基本用法:

http://blog.csdn.net/jay100500/article/details/70767007


参考文献

[1]Picasso官方简易Wiki.Picasso[OL].2013.http://square.github.io/picasso/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

持续学习的工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值