Picasso源碼

我們知道啡专,Glide和Picasso是兩個非常優(yōu)秀的圖片加載框架冲杀,本篇先分析Picasso的源碼,后面再用一篇文對Glide進行分析身堡。兩者的使用方式雖然差不多邓尤,但源碼實現(xiàn)有很大不同,下面先對兩者做一個對比贴谎,然后再分析Picasso源碼:

Glide和Picasso對比

  • Picasso代碼體積要比 Glide小很多汞扎;
  • Picasso緩存完整大小圖片到本地,Glide則根據(jù)不同控件大小擅这,緩存多張圖片澈魄;
  • Picasso占用更大緩存;
  • 未緩存前仲翎,Picasso加載速度更快痹扇,緩存后,則Glide更快溯香;
  • Glide可以加載Gif和視頻縮略圖鲫构,Picasso則不能。

示例

    Picasso.get()
           .load( "http://vxKM8h8Hx2wWx3xW.jpg" )
           .placeholder( R.drawable.ic_launcher_background )
           .error( R.drawable.ic_launcher_background )
           .resize( 480,480 )
           .centerCrop()
           .rotate( 360 )
           .priority( Picasso.Priority.HIGH )
           .tag( "tag" )
           .memoryPolicy( MemoryPolicy.NO_CACHE )
           .networkPolicy( NetworkPolicy.NO_CACHE )
           .into( ivImage );

源碼分析

public static Picasso get() {
    if (singleton == null) {
      synchronized (Picasso.class) {
        if (singleton == null) {
          if (PicassoProvider.context == null) {
            throw new IllegalStateException("context == null");
          }
          singleton = new Builder(PicassoProvider.context).build();
        }
      }
    }
    return singleton;
  }

Picassso使用的也是雙重鎖檢測的單例模式玫坛,并且用Builder構建模式來構造结笨,這種創(chuàng)建模式在前面幾篇源碼分析已見過多次了。來看Builder如何構造的湿镀。

public class Picasso {
  ......
  public static class Builder {
    //上下文
    private final Context context;
    .....

  //構造方法
    public Builder(@NonNull Context context) {
      if (context == null) {
        throw new IllegalArgumentException("Context must not be null.");
      }
      this.context = context.getApplicationContext();
    }


    public Picasso build() {
      Context context = this.context;
      //使用ok下載
      if (downloader == null) {
        downloader = new OkHttp3Downloader(context);
      }
      //lru緩存
      if (cache == null) {
        cache = new LruCache(context);
      }
      //線程池
      if (service == null) {
        service = new PicassoExecutorService();
      }
      //請求轉(zhuǎn)換器
      if (transformer == null) {
        transformer = RequestTransformer.IDENTITY;
      }
      //緩存狀態(tài)
      Stats stats = new Stats(cache);
      //分發(fā)器
      Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
      //構造Picasso
      return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
          defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
    }
  }

Builder構造方法只接收一個上下文禀梳,并且獲取的是Application的上下文,這樣做可以防止內(nèi)存泄漏肠骆∷阃荆看到build方法知道,Picasso用的是OkHttp框架來進行加載的蚀腿。

在方法最后有一個Dispatcher 類嘴瓤,它是Picasso的核心類,在構造時傳遞了一個HANDLER對象莉钙,為主線程Handler廓脆,這個對象我們后面再進行分析。而在Dispatcher 內(nèi)部還將創(chuàng)建一個子線程Handler對象磁玉,因此停忿,Dispatcher 將持有主線程Handler和子線程Handler,完成主線程和子線程的切換蚊伞,下面先分析Dispatcher的創(chuàng)建席赂。

class Dispatcher {
  ......
  //子線程
  final DispatcherThread dispatcherThread;
  //子線程Handler
  final Handler handler;
  //主線程Handler
  final Handler mainThreadHandler;
  ......

   Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
      Downloader downloader, Cache cache, Stats stats) {
    //創(chuàng)建DispatcherThread線程吮铭,并開啟
    this.dispatcherThread = new DispatcherThread();
    this.dispatcherThread.start();
    Utils.flushStackLocalLeaks(dispatcherThread.getLooper());
    this.context = context;
    this.service = service;
    this.hunterMap = new LinkedHashMap<>();
    this.failedActions = new WeakHashMap<>();
    this.pausedActions = new WeakHashMap<>();
    this.pausedTags = new LinkedHashSet<>();
    //子線程Handler
    this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
    this.downloader = downloader;
    //主線程Handler
    this.mainThreadHandler = mainThreadHandler;
    this.cache = cache;
    this.stats = stats;
    this.batch = new ArrayList<>(4);
    this.airplaneMode = Utils.isAirplaneModeOn(this.context);
    this.scansNetworkChanges = hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);
    //網(wǎng)絡監(jiān)聽廣播
    this.receiver = new NetworkBroadcastReceiver(this);
    receiver.register();
    }

  ......

  static class DispatcherThread extends HandlerThread {
    DispatcherThread() {
      super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
    }
  }

  ......
}

DispatcherThread是Dispatcher的靜態(tài)內(nèi)部類,它繼承HandlerThread颅停。

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private Handler mHandler;
    
    public HandlerThread(String name, int priority) {
        super(name);
        this.mPriority = priority;
    }

    protected void onLooperPrepared() {
    }

  //run方法
    public void run() {
        //得到當前線程id
        this.mTid = Process.myTid();
        //獲取Looper谓晌,并保存
        Looper.prepare();
        synchronized(this) {
            //獲取當前線程Looper
            this.mLooper = Looper.myLooper();
            //喚醒線程
            this.notifyAll();
        }
        Process.setThreadPriority(this.mPriority);
        this.onLooperPrepared();
        //開始消息循環(huán)
        Looper.loop();
        this.mTid = -1;
    }

    //獲取Looper
    public Looper getLooper() {
        if (!this.isAlive()) {
            return null;
        } else {
            synchronized(this) {
                //線程活著,Looper未創(chuàng)建
                while(this.isAlive() && this.mLooper == null) {
                    try {
                        //Looper未創(chuàng)建癞揉,阻塞
                        this.wait();
                    } catch (InterruptedException var4) {
                    }
                }
            }
            return this.mLooper;
        }
    }

    //獲取當前線程Handler
    public Handler getThreadHandler() {
        if (this.mHandler == null) {
            this.mHandler = new Handler(this.getLooper());
        }
        return this.mHandler;
    }

   .....
}

HandlerThread是系統(tǒng)實現(xiàn)的一個線程類纸肉,在《Handler,Looper喊熟,Message》以及《ThreadLocal》中已經(jīng)有詳細的分析柏肪,在這里,HandlerThread作為單獨使用芥牌,用于子線程的消息循環(huán)烦味。

由Dispatcher的構造方法看到,它持有了兩個Handler胳泉,一個是主線程的Handler拐叉,一個是關聯(lián)HandlerThread的Looper所得到的,子線程Handler扇商。主線程Handler凤瘦,在上面分析Builder的build方法時提過,將其傳遞給了Dispatcher案铺。

public class Picasso {
  ......

  static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
    @Override public void handleMessage(Message msg) {
        ......
    }
  }
}
class Dispatcher {
    ......
  private static class DispatcherHandler extends Handler {
    private final Dispatcher dispatcher;

    DispatcherHandler(Looper looper, Dispatcher dispatcher) {
      super(looper);
      this.dispatcher = dispatcher;
    }

在構造方法最后蔬芥,創(chuàng)建了一個NetworkBroadcastReceiver對象,它是用來監(jiān)聽當前網(wǎng)絡情況的廣播控汉。我們先來看它的實現(xiàn)笔诵。

class Dispatcher {
  static final int NETWORK_STATE_CHANGE = 9;
  ......
  static class NetworkBroadcastReceiver extends BroadcastReceiver {
    static final String EXTRA_AIRPLANE_STATE = "state";
    //持有分發(fā)器
    private final Dispatcher dispatcher;
    NetworkBroadcastReceiver(Dispatcher dispatcher) {
      this.dispatcher = dispatcher;
    }
    //注冊廣播
    void register() {
      IntentFilter filter = new IntentFilter();
      filter.addAction(ACTION_AIRPLANE_MODE_CHANGED);
      if (dispatcher.scansNetworkChanges) {
        filter.addAction(CONNECTIVITY_ACTION);
      }
      dispatcher.context.registerReceiver(this, filter);
    }
    //注銷廣播
    void unregister() {
      dispatcher.context.unregisterReceiver(this);
    }

    //接收方法
    @SuppressLint("MissingPermission")
    @Override public void onReceive(Context context, Intent intent) {
     if (intent == null) {
        return;
      }
      final String action = intent.getAction();
      //飛行模式
      if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
        if (!intent.hasExtra(EXTRA_AIRPLANE_STATE)) {
          return; 
        }
        dispatcher.dispatchAirplaneModeChange(intent.getBooleanExtra(EXTRA_AIRPLANE_STATE, false));
      //正常連接模式
      } else if (CONNECTIVITY_ACTION.equals(action)) {
        ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
      //調(diào)用dispatcher的dispatchNetworkStateChange方法
        dispatcher.dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
      }
    }
  }

  ......
  //向子線程Handler發(fā)送NetworkInfo
  void dispatchNetworkStateChange(NetworkInfo info) {
    handler.sendMessage(handler.obtainMessage(NETWORK_STATE_CHANGE, info));
  }

  ......

網(wǎng)絡有變化情況下,廣播將接收到消息姑子。網(wǎng)絡正常連接情況下乎婿,將通過Dispatcher的dispatchNetworkStateChange方法,向子線程Handler發(fā)送編號為NETWORK_STATE_CHANGE的消息體街佑。上面分析知道谢翎,這個Handler為DispatcherHandler對象。

class Dispatcher {
    ......
  private static class DispatcherHandler extends Handler {
    private final Dispatcher dispatcher;
    ......
    case NETWORK_STATE_CHANGE: {
          NetworkInfo info = (NetworkInfo) msg.obj;
          dispatcher.performNetworkStateChange(info);
          break;
        }
    ......
}

//處理網(wǎng)絡變化
void performNetworkStateChange(NetworkInfo info) {
    //是否PicassoExecutorService線程池
    if (service instanceof PicassoExecutorService) {
          ((PicassoExecutorService) service).adjustThreadCount(info);
      }
   
    if (info != null && info.isConnected()) {
      flushFailedActions();
    }
  }

如果使用了PicassoExecutorService線程池沐旨,將調(diào)用線程池的adjustThreadCount方法森逮,我們看它做了什么工作。

class PicassoExecutorService extends ThreadPoolExecutor {
  //默認核心數(shù)為3
  private static final int DEFAULT_THREAD_COUNT = 3;
  PicassoExecutorService() {
    super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
        new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
  }

  void adjustThreadCount(NetworkInfo info) {
    if (info == null || !info.isConnectedOrConnecting()) {
      setThreadCount(DEFAULT_THREAD_COUNT);
      return;
    }
    switch (info.getType()) {
      case ConnectivityManager.TYPE_WIFI: //wifi
      case ConnectivityManager.TYPE_WIMAX:
      case ConnectivityManager.TYPE_ETHERNET://以太網(wǎng)
        setThreadCount(4);
        break;
      case ConnectivityManager.TYPE_MOBILE:
        switch (info.getSubtype()) {
          case TelephonyManager.NETWORK_TYPE_LTE:  // 4G
          case TelephonyManager.NETWORK_TYPE_HSPAP:
          case TelephonyManager.NETWORK_TYPE_EHRPD:
            setThreadCount(3);
            break;
          case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
          case TelephonyManager.NETWORK_TYPE_CDMA:
          case TelephonyManager.NETWORK_TYPE_EVDO_0:
          case TelephonyManager.NETWORK_TYPE_EVDO_A:
          case TelephonyManager.NETWORK_TYPE_EVDO_B:
            setThreadCount(2);
            break;
          case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
          case TelephonyManager.NETWORK_TYPE_EDGE:
            setThreadCount(1);
            break;
          default:
            setThreadCount(DEFAULT_THREAD_COUNT);
        }
        break;
      default:
        setThreadCount(DEFAULT_THREAD_COUNT);
    }
  }

//設置核心數(shù)和最大線程池數(shù)
 private void setThreadCount(int threadCount) {
    setCorePoolSize(threadCount);
    setMaximumPoolSize(threadCount);
  }

adjustThreadCount會根據(jù)當前網(wǎng)絡的連接情況磁携,設置線程的核心數(shù)和最大線程池數(shù)褒侧。

回到Builder類,build方法被調(diào)用,在最后創(chuàng)建分發(fā)器Dispatcher對象后闷供,便開始構建Picasso 對象烟央,

public class Picasso {
    ......

   Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
      RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
      Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
    
    this.context = context;
    this.dispatcher = dispatcher;
    this.cache = cache;
    this.listener = listener;
    this.requestTransformer = requestTransformer;
    this.defaultBitmapConfig = defaultBitmapConfig;

    int builtInHandlers = 7;
    int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
    //RequestHandler集合
    List<RequestHandler> allRequestHandlers = new ArrayList<>(builtInHandlers + extraCount);
    //添加不同的RequestHandler
    allRequestHandlers.add(new ResourceRequestHandler(context));
    if (extraRequestHandlers != null) {
      allRequestHandlers.addAll(extraRequestHandlers);
    }
    allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
    allRequestHandlers.add(new MediaStoreRequestHandler(context));
    allRequestHandlers.add(new ContentStreamRequestHandler(context));
    allRequestHandlers.add(new AssetRequestHandler(context));
    allRequestHandlers.add(new FileRequestHandler(context));
    allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
    requestHandlers = Collections.unmodifiableList(allRequestHandlers);

    ......
  }

allRequestHandlers 列表保存了不同的RequestHandler。從命名可以知道这吻,根據(jù)不同的源吊档,將使用不同的RequestHandler來加載圖片篙议。比如唾糯,當我們從網(wǎng)絡獲取一張圖片時,將使用NetworkRequestHandler來進行加載鬼贱。

通過靜態(tài)get()方法便得到了構建的Picasso 對象移怯,接著我們調(diào)用load()方法,傳入圖片的加載路徑这难。

public class Picasso {
  ......

  public RequestCreator load(@Nullable String path) {
    if (path == null) {
      return new RequestCreator(this, null, 0);
    }
    if (path.trim().length() == 0) {
      throw new IllegalArgumentException("Path must not be empty.");
    }
    return load(Uri.parse(path));
  }

//封裝地址為Uri舟误,并構建RequestCreator
public RequestCreator load(@Nullable Uri uri) {
    return new RequestCreator(this, uri, 0);
  }
......
}

load方法返回了RequestCreator對象,見名知義姻乓,我們可以看出它是用來創(chuàng)建Request對象的嵌溢。我們先看它的構造方法。

public class RequestCreator {

    ......
   private final Picasso picasso;
   private final Request.Builder data;
    ......

   RequestCreator(Picasso picasso, Uri uri, int resourceId) {
    if (picasso.shutdown) {
      throw new IllegalStateException(
          "Picasso instance already shut down. Cannot submit new requests.");
    }
    this.picasso = picasso;
    //創(chuàng)建了Request.Builder
    this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
  }

  ......

}

RequestCreator的構造方法做了兩個操作蹋岩,將Picasso 保存到成員變量中赖草,并將uri, resourceId, picasso.defaultBitmapConfig三個參數(shù),傳遞給Request.Builder剪个。像裁剪秧骑、旋轉(zhuǎn)、根據(jù)控件縮放都是通過Request.Builder來操作的扣囊,在這里乎折,它的還尚未執(zhí)行build方法來構建Request,而是在into方法中被構建的侵歇,因為骂澄,在沒有準備好執(zhí)行請求前,無需提前創(chuàng)建惕虑,消耗資源坟冲。

那么,接著來看into方法是如何獲取圖片枷遂,并設置到我們指定的控件中的樱衷。

public class RequestCreator {

    .......

  //調(diào)用重載into
  public void into(ImageView target) {
      into(target, null);
  }

  //重載into
  public void into(ImageView target, Callback callback) {
    long started = System.nanoTime();
    checkMain();
    //控件不能為null
    if (target == null) {
      throw new IllegalArgumentException("Target must not be null.");
    }
    //如果url為null或resourceId為0,則取消請求
    if (!data.hasImage()) {
      //取消請求
      picasso.cancelRequest(target);
      //顯示設置的占位圖片
      if (setPlaceholder) {
        setPlaceholder(target, getPlaceholderDrawable());
      }
      return;
    }

    //是否設置fit酒唉,用于延遲
    if (deferred) {
      if (data.hasSize()) {
        throw new IllegalStateException("Fit cannot be used with resize.");
      }
      int width = target.getWidth();
      int height = target.getHeight();
      //是否能獲取到寬高
      if (width == 0 || height == 0) {
        if (setPlaceholder) {
          setPlaceholder(target, getPlaceholderDrawable());
        }
        //創(chuàng)建DeferredRequestCreator
        picasso.defer(target, new DeferredRequestCreator(this, target, callback));
        return;
      }
      data.resize(width, height);
    }

    //創(chuàng)建Request 
    Request request = createRequest(started);
    //將Request參數(shù)構建成一個字符串矩桂,作為key
    String requestKey = createKey(request);
    //從緩存獲取圖片
    if (shouldReadFromMemoryCache(memoryPolicy)) {
      Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
      //如果緩存圖片存在
      if (bitmap != null) {
        //取消請求
        picasso.cancelRequest(target);
        setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
        if (picasso.loggingEnabled) {
          log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
        }
        //成功回調(diào)
        if (callback != null) {
          callback.onSuccess();
        }
        return;
      }
    }

    if (setPlaceholder) {
      setPlaceholder(target, getPlaceholderDrawable());
    }

    //緩存沒有命中,則構建一個Action 
    Action action =
        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
            errorDrawable, requestKey, tag, callback, noFade);
    //提交請求
    picasso.enqueueAndSubmit(action);
  }

 //創(chuàng)建Request 
  private Request createRequest(long started) {
    int id = nextId.getAndIncrement();
    //執(zhí)行Request.Builder的build方法,構建Request 
    Request request = data.build();
    request.id = id;
    request.started = started;

    boolean loggingEnabled = picasso.loggingEnabled;
    if (loggingEnabled) {
      log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());
    }
    //如果沒有設置變換Request將不做變化返回
    Request transformed = picasso.transformRequest(request);
    if (transformed != request) {
      transformed.id = id;
      transformed.started = started;

      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);
      }
    }
    //返回Request 
    return transformed;
  }


}

into方法主要進行3個判斷:

  • 判斷是否設置fit 屬性侄榴,如果設置了雹锣,嘗試獲取ImageView 的寬高,如果獲取不到癞蚕,生成一個DeferredRequestCreator(延遲的請求管理器)蕊爵,在DeferredRequestCreator中當監(jiān)聽到可以獲取ImageView 的寬高的時候,再執(zhí)行into方法桦山。

  • 判斷是否從內(nèi)存緩存獲取圖片攒射,如果沒有設置NO_CACHE,則從內(nèi)存獲取,命中直接回調(diào)CallBack 并且顯示圖片恒水。

  • 如果緩存未命中会放,代碼執(zhí)行到最后,會生成一個Action并提交钉凌,用于網(wǎng)絡請求咧最。

每一個 Target 同一時刻只對應一個 Action,當同一個 Target 有新的請求時御雕,當前 Action 會被取消掉矢沿,這可以解決列表復用錯亂問題。它的實現(xiàn)子類為ImageViewAction酸纲。

abstract class Action<T> {
  //弱引用
  static class RequestWeakReference<M> extends WeakReference<M> {
    final Action action;
    RequestWeakReference(Action action, M referent, ReferenceQueue<? super M> q) {
      super(referent, q);
      this.action = action;
    }
  }
    ...... 
  final WeakReference<T> target;
    ......

  Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
      int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
    this.picasso = picasso;
    this.request = request;
    //將target包裝為弱引用
    this.target =
        target == null ? null : new RequestWeakReference<>(this, target, picasso.referenceQueue);
    ......
  }

  abstract void complete(Bitmap result, Picasso.LoadedFrom from);
  abstract void error(Exception e);
    ......
}

class ImageViewAction extends Action<ImageView> {
  Callback callback;
  ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
      int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
      Callback callback, boolean noFade) {
    super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
        tag, noFade);
    this.callback = callback;
  }

  @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
    if (result == null) {
      throw new AssertionError(
          String.format("Attempted to complete action with no result!\n%s", this));
    }
    //獲取ImageView
    ImageView target = this.target.get();
    if (target == null) {
      return;
    }
    Context context = picasso.context;
    boolean indicatorsEnabled = picasso.indicatorsEnabled;
    //設置圖片到ImageView
    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    //回調(diào)
    if (callback != null) {
      callback.onSuccess();
    }
  }

Action對象用一個弱應用封裝了Target捣鲸。當

public class Picasso {
  //Action緩存集合
  final Map<Object, Action> targetToAction;
  ......

  void enqueueAndSubmit(Action action) {
    //獲取Target
    Object target = action.getTarget();
    //target 是否關聯(lián)了其他action,如果不相等福青,說明關聯(lián)了
    if (target != null && targetToAction.get(target) != action) {
      // 先取消之前的關聯(lián)
      cancelExistingRequest(target);
      //重新關聯(lián)映射
      targetToAction.put(target, action);
    }
    //提交Action
    submit(action);
  }
  }

將Action提交給Picasso 的enqueueAndSubmit后摄狱,會先判斷當前的Target是否已關聯(lián)了其他Action,如果是的話无午,通過cancelExistingRequest(target)方法媒役,將之前的關聯(lián)取消,再將Target和提交過來的Action重新關聯(lián)宪迟,并緩存起來酣衷。那么,cancelExistingRequest(target)是如何取消它們的關聯(lián)的呢次泽。

  //取消Target的請求
  void cancelExistingRequest(Object target) {
    //檢查是否在主線程
    checkMain();
    //從緩存中移除target對應的action
    Action action = targetToAction.remove(target);
    if (action != null) {
    //將Action的cancelled置為true穿仪,如果有設置callback,則callback置null
      action.cancel();
    //分發(fā)器執(zhí)行取消操作
      dispatcher.dispatchCancel(action);
    }
    //如果是Target是ImageView意荤,則從DeferredRequestCreator 移除
    if (target instanceof ImageView) {
      ImageView targetImageView = (ImageView) target;
      DeferredRequestCreator deferredRequestCreator =
          targetToDeferredRequestCreator.remove(targetImageView);
      if (deferredRequestCreator != null) {
        deferredRequestCreator.cancel();
      }
    }

將Action從緩存移除后啊片,主要是通過Dispatcher 分發(fā)器來取消Action的其他執(zhí)行流程。Dispatcher 通過子線程DispatcherHandler對象來處理玖像。

class Dispatcher {
  //BitmapHunter緩存
  final Map<String, BitmapHunter> hunterMap;
    ......
    //發(fā)送取消消息
   void dispatchCancel(Action action) {
    handler.sendMessage(handler.obtainMessage(REQUEST_CANCEL, action));
    }

  //子線程Handler
  private static class DispatcherHandler extends Handler {
    private final Dispatcher dispatcher;
    @Override public void handleMessage(final Message msg) {
      switch (msg.what) {
         ......
        case REQUEST_CANCEL: {
          Action action = (Action) msg.obj;
          dispatcher.performCancel(action);
          break;
        }
    }}
}

//執(zhí)行Action 具體取消操作
void performCancel(Action action) {
    //獲取key
    String key = action.getKey();
    //根據(jù)key獲取BitmapHunter 紫谷,它是一個Runnable
    BitmapHunter hunter = hunterMap.get(key);
    if (hunter != null) {
      //從BitmapHunter 的actions列表移除Action
      hunter.detach(action);
      if (hunter.cancel()) {
        hunterMap.remove(key);
        if (action.getPicasso().loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_CANCELED, action.getRequest().logId());
        }
      }
    }
 // 如果在 pause 隊列中,則移除
    if (pausedTags.contains(action.getTag())) {
      pausedActions.remove(action.getTarget());
      if (action.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_CANCELED, action.getRequest().logId(),
            "because paused request got canceled");
      }
    }
  // 失敗隊列中包含了,也移除
    Action remove = failedActions.remove(action.getTarget());
    if (remove != null && remove.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_CANCELED, remove.getRequest().logId(), "from replaying");
    }
  }

BitmapHunter實現(xiàn)了Runnable笤昨,而上面分析Dispatcher構造過程時知道祖驱,Dispatcher對象的成員變量service,持有線程池ExecutorService應用瞒窒,BitmapHunter將作為其執(zhí)行單位捺僻。

我們回到enqueueAndSubmit中的submit(action)方法。action 將被提交給Dispatcher崇裁,Dispatcher則又進一步通過DispatcherHandler 來處理消息匕坯。

public class Picasso {
  ......

  void submit(Action action) {
    dispatcher.dispatchSubmit(action);
  }
class Dispatcher {
  ......
  
  void dispatchSubmit(Action action) {
    handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
  }

  //子線程Handler
  private static class DispatcherHandler extends Handler {
    private final Dispatcher dispatcher;
    @Override public void handleMessage(final Message msg) {
      switch (msg.what) {
         case REQUEST_SUBMIT: {
          Action action = (Action) msg.obj;
          dispatcher.performSubmit(action);
          break;
        }
    }}
  
  void performSubmit(Action action) {
        performSubmit(action, true);
  }

  void performSubmit(Action action, boolean dismissFailed) {
   // 查看保存暫停tag表里面沒有包含Action的tag,如果包含,則將Action 存到暫停Action表里
    if (pausedTags.contains(action.getTag())) {
      pausedActions.put(action.getTarget(), action);
      if (action.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
            "because tag '" + action.getTag() + "' is paused");
      }
      return;
    }
    //已經(jīng)提交過寇壳,從hunterMap中獲取
    BitmapHunter hunter = hunterMap.get(action.getKey());
    if (hunter != null) {
      hunter.attach(action);
      return;
    }

    //線程池被關閉了
    if (service.isShutdown()) {
      if (action.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
      }
      return;
    }
    //創(chuàng)建一個新的BitmapHunter 
    hunter = forRequest(action.getPicasso(), this, cache, stats, action);
    //BitmapHunter 提交給線程池
    hunter.future = service.submit(hunter);
    //根據(jù)Key緩存在hunterMap中
    hunterMap.put(action.getKey(), hunter);
    if (dismissFailed) {
      failedActions.remove(action.getTarget());
    }
    if (action.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
    }
  }
 }

performSubmit中關鍵的邏輯是醒颖,創(chuàng)建 BitmapHunter 并提交給線程池ExecuteService妻怎。forRequest 方法構建了一個BitmapHunter 壳炎,在構建過程中需要為BitmapHunter 指定對應的處理器,即我們前面提到過的RequestHandler逼侦。

class BitmapHunter implements Runnable {

    ......

  //獲取BitmapHunter 
  static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
      Action action) {
    //獲取Request 
    Request request = action.getRequest();
    List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
    //遍歷requestHandlers 列表
    for (int i = 0, count = requestHandlers.size(); i < count; i++) {
      RequestHandler requestHandler = requestHandlers.get(i);
      //查看那種RequestHandler能處理這個請求
      if (requestHandler.canHandleRequest(request)) {
        //將對應的RequestHandler 封裝到BitmapHunter中返回
        return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
      }
    }
    //如果獲取到合適的RequestHandler 則ERRORING_HANDLER
    return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
  }
}

這里假設通過網(wǎng)絡獲取圖片匿辩,那么將匹配到NetworkRequestHandler,那么它的canHandleRequest方法是如何比對request榛丢,以決定使用NetworkRequestHandler處理器的铲球。

class NetworkRequestHandler extends RequestHandler {
  private static final String SCHEME_HTTP = "http";
  private static final String SCHEME_HTTPS = "https";
  
  //判斷是否使用當前處理器
   @Override public boolean canHandleRequest(Request data) {
    String scheme = data.uri.getScheme();
    return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
  }

  ......

}

邏輯很簡單,判斷scheme 是否為http或https晰赞。

到這里BitmapHunter構建成功后稼病,被提交給了Dispatcher 的線程池調(diào)用,也就是說掖鱼,后續(xù)請求的邏輯將在BitmapHunter的run方法中執(zhí)行然走。

class BitmapHunter implements Runnable {
  //保存為位圖
  Bitmap result;
    ......

  @Override public void run() {
    try {
      updateThreadName(data);

      if (picasso.loggingEnabled) {
        log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
      }
      //具體獲取邏輯又被封裝成hunt方法
      result = hunt();

      //result為null,分發(fā)失敗
      if (result == null) {
        dispatcher.dispatchFailed(this);
      } else {
      //result不為null戏挡,顯示圖片
        dispatcher.dispatchComplete(this);
      }
    } catch (NetworkRequestHandler.ResponseException e) {
      if (!NetworkPolicy.isOfflineOnly(e.networkPolicy) || e.code != 504) {
        exception = e;
      }
      dispatcher.dispatchFailed(this);
    } catch (IOException e) {
      exception = e;
      dispatcher.dispatchRetry(this);
    } catch (OutOfMemoryError e) {
      StringWriter writer = new StringWriter();
      stats.createSnapshot().dump(new PrintWriter(writer));
      exception = new RuntimeException(writer.toString(), e);
      dispatcher.dispatchFailed(this);
    } catch (Exception e) {
      exception = e;
      dispatcher.dispatchFailed(this);
    } finally {
      Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
    }
  }

//獲取Bitmap
 Bitmap hunt() throws IOException {
    Bitmap bitmap = null;
  // 從內(nèi)存緩存獲取Bitmap
    if (shouldReadFromMemoryCache(memoryPolicy)) {
      bitmap = cache.get(key);
      if (bitmap != null) {
        stats.dispatchCacheHit();
        loadedFrom = MEMORY;
        if (picasso.loggingEnabled) {
          log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
        }
        return bitmap;
      }
    }
    //緩存沒有芍瑞,則通過處理器加載
    networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
    //處理器加載
    RequestHandler.Result result = requestHandler.load(data, networkPolicy);
    if (result != null) {
      loadedFrom = result.getLoadedFrom();
      exifOrientation = result.getExifOrientation();
      bitmap = result.getBitmap();
      //重新解碼源 
      if (bitmap == null) {
        Source source = result.getSource();
        try {
          bitmap = decodeStream(source, data);
        } finally {
          try {
            source.close();
          } catch (IOException ignored) {
          }
        }
      }
    }

    ......

    return bitmap;
  }

如果緩存中沒有獲取到Bitmap,則將通過處理器NetworkRequestHandler 的load方法從網(wǎng)絡進行加載褐墅。如果Bitmap獲取成功拆檬,將通過dispatchComplete方法將Bitamp設置到控件中。我們先分析Bitmap從網(wǎng)絡獲取的流程妥凳。

class NetworkRequestHandler extends RequestHandler {
  private final Downloader downloader;
    ......

 @Override public Result load(Request request, int networkPolicy) throws IOException {
    okhttp3.Request downloaderRequest = createRequest(request, networkPolicy);
    //上面分析知道竟贯,downloader實際為OkHttp3Downloader
    Response response = downloader.load(downloaderRequest);
    ResponseBody body = response.body();
        ......

    return new Result(body.source(), loadedFrom);
  }

在最新的Picasso源碼中,OkHttp3Downloader已經(jīng)是Downloader 的唯一實現(xiàn)類逝钥,原來的UrlConnectionDownloader 已經(jīng)被移除調(diào)了屑那。

public final class OkHttp3Downloader implements Downloader {
    .....

 @NonNull @Override public Response load(@NonNull Request request) throws IOException {
    return client.newCall(request).execute();
  }
    ......
}

OkHttp3Downloader通過同步請求進行圖片下載,OkHttp的具體源碼執(zhí)行流程已分析過,這里不做贅述齐莲。

從網(wǎng)絡下載后痢站,Source將被解析成Bitmap,并返回給BitmapHunter 的成員變量result选酗,緊接著調(diào)用Dispatcher的dispatchComplete方法阵难,展示到控件中。

class Dispatcher {
    ......
 final List<BitmapHunter> batch;
private static final int BATCH_DELAY = 200; // ms
    ......

  void dispatchComplete(BitmapHunter hunter) {
      handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
  }


  private static class DispatcherHandler extends Handler {
    private final Dispatcher dispatcher;
   
    DispatcherHandler(Looper looper, Dispatcher dispatcher) {
      super(looper);
      this.dispatcher = dispatcher;
    }

    @Override public void handleMessage(final Message msg) {
          .......
      //從BitmapHunter 獲取Bitmap
      case HUNTER_COMPLETE: {
          BitmapHunter hunter = (BitmapHunter) msg.obj;
          dispatcher.performComplete(hunter);
          break;
        }

      //批量處理Bitmap
      case HUNTER_DELAY_NEXT_BATCH: {
          dispatcher.performBatchComplete();
          break;
        }

   }

  void performComplete(BitmapHunter hunter) {
    //如果我們設置了緩存芒填,則將Bitmap緩存起來
    if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
      cache.set(hunter.getKey(), hunter.getResult());
    }
    //將BitmapHunter移除掉
    hunterMap.remove(hunter.getKey());
    batch(hunter);
    if (hunter.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
    }
  }

  private void batch(BitmapHunter hunter) {
    //cancel為true就不展示了
    if (hunter.isCancelled()) {
      return;
    }
    if (hunter.result != null) {
      hunter.result.prepareToDraw();
    }
    //將hunter添加到列表中
    batch.add(hunter);
    //消息列表中沒有HUNTER_DELAY_NEXT_BATCH的類型
    if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
      handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
    }
  }

  void performBatchComplete() {
    //將batch拷貝一份
    List<BitmapHunter> copy = new ArrayList<>(batch);
    //清理掉原來的list
    batch.clear();
    //將拷貝后的List發(fā)送給主線程Handler
    mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
    logBatch(copy);
  }

}

因為可能存在多個圖片同時下載完成的情況呜叫,因此,在向主線程發(fā)送消息前殿衰,會將接收到的BitmapHunter集中添加到List中朱庆,一起發(fā)送給主線程的Handler進行處理。由上面分析知道闷祥,這個mainThreadHandler在Picasso 中娱颊,在構造Dispatcher對象時被傳遞進來。我們看它消息類型為HUNTER_BATCH_COMPLETE是如何處理圖片的展示的凯砍。

public class Picasso {
  ......

  static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
    @Override public void handleMessage(Message msg) {
      switch (msg.what) {
        case HUNTER_BATCH_COMPLETE: {
          //獲取到BitmapHunter列表箱硕,進行遍歷
          @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
          for (int i = 0, n = batch.size(); i < n; i++) {
            BitmapHunter hunter = batch.get(i);
            hunter.picasso.complete(hunter);
          }
          break;
        }
        .....
    }
  };

  void complete(BitmapHunter hunter) {
    //獲取Action
    Action single = hunter.getAction();
    List<Action> joined = hunter.getActions();
    boolean hasMultiple = joined != null && !joined.isEmpty();
    boolean shouldDeliver = single != null || hasMultiple;
    if (!shouldDeliver) {
      return;
    }

    Uri uri = hunter.getData().uri;
    Exception exception = hunter.getException();
    Bitmap result = hunter.getResult();
    LoadedFrom from = hunter.getLoadedFrom();

    //將Action 傳遞給deliverAction
    if (single != null) {
        deliverAction(result, from, single, exception);
    }

   ......
  }


  private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
    if (action.isCancelled()) {
      return;
    }
    if (!action.willReplay()) {
      targetToAction.remove(action.getTarget());
    }
    if (result != null) {
      if (from == null) {
        throw new AssertionError("LoadedFrom cannot be null.");
      }
      //調(diào)用action的complete設置圖片
      action.complete(result, from);
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);
      }
    } else {
      action.error(e);
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_ERRORED, action.request.logId(), e.getMessage());
      }
    }
  }
}

主線程的HANDLER 接收到BitmapHunter列表后,進行遍歷悟衩,并將Bitmap傳遞給對應Action的complete方法剧罩,將其設置到Target中,在示例中我們傳遞了ImageView座泳,因此锁施,Action的實際實現(xiàn)類為ImageViewAction挚冤,我們看它的complete方法。

class ImageViewAction extends Action<ImageView> {
    ......
   @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
    if (result == null) {
      throw new AssertionError(
          String.format("Attempted to complete action with no result!\n%s", this));
    }
    //獲取ImageView 
    ImageView target = this.target.get();
    if (target == null) {
      return;
    }
      Context context = picasso.context;
    boolean indicatorsEnabled = picasso.indicatorsEnabled;
    //通過PicassoDrawable設置Bitmap
    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    if (callback != null) {
      callback.onSuccess();
    }
  }
}
final class PicassoDrawable extends BitmapDrawable {
    ......
  static void setBitmap(ImageView target, Context context, Bitmap bitmap,
      Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
    //移除占位圖
    Drawable placeholder = target.getDrawable();
    if (placeholder instanceof Animatable) {
      ((Animatable) placeholder).stop();
    }
    //將Bitmap轉(zhuǎn)為PicassoDrawable 
    PicassoDrawable drawable =
        new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
    target.setImageDrawable(drawable);
  }
  ......
}

BitmapDrawable繼承自BitmapDrawable ,最終將Bitmap轉(zhuǎn)為PicassoDrawable 审磁,設置到target中没炒,即ImageView 帜矾,到此豺型,圖片就顯示到我們的屏幕上了。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饼齿,一起剝皮案震驚了整個濱河市饲漾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缕溉,老刑警劉巖考传,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異证鸥,居然都是意外死亡僚楞,警方通過查閱死者的電腦和手機勤晚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泉褐,“玉大人赐写,你說我怎么就攤上這事∧ぴ撸” “怎么了挺邀?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長跳座。 經(jīng)常有香客問我端铛,道長,這世上最難降的妖魔是什么疲眷? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任禾蚕,我火速辦了婚禮,結(jié)果婚禮上狂丝,老公的妹妹穿的比我還像新娘换淆。我一直安慰自己,他們只是感情好美侦,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布产舞。 她就那樣靜靜地躺著,像睡著了一般菠剩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上耻煤,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天具壮,我揣著相機與錄音,去河邊找鬼哈蝇。 笑死棺妓,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的炮赦。 我是一名探鬼主播怜跑,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吠勘!你這毒婦竟也來了性芬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤剧防,失蹤者是張志新(化名)和其女友劉穎植锉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體峭拘,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡俊庇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年狮暑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辉饱。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡搬男,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出彭沼,到底是詐尸還是另有隱情止后,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布溜腐,位于F島的核電站译株,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挺益。R本人自食惡果不足惜歉糜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望望众。 院中可真熱鬧匪补,春花似錦、人聲如沸烂翰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽甘耿。三九已至踊兜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間佳恬,已是汗流浹背捏境。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留毁葱,地道東北人垫言。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像倾剿,于是被迫代替她去往敵國和親筷频。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容