Picasso源码初步分析

Picasso工作原理

我们知道使用Picasso最简单的方式来显示一张图片的方法就是如下了


    Picasso.with(this).load("http://192.168.56.1:8080/1.jpg").into(iv) ;

我们就从Picasso.with(this)这句话开始分析,虽然只是简单的一句代码,但是里面的含义却不少,看源码:


    public static Picasso with(@NonNull Context context) {
    if (context == null) {
      throw new IllegalArgumentException("context == null");
    }
    if (singleton == null) {
      synchronized (Picasso.class) {
        if (singleton == null) {
          singleton = new Builder(context).build();
        }
      }
    }
    return singleton;
  }


由源码可以看出,这个一个双检查机制的单例。再看singleton的申明,


    static volatile Picasso singleton = null;

它被volatile修饰,由于在jdk1.5之前中cache、寄存器到主存的写顺序的规定和java编译器允许处理器乱序执行,因此上面的操作并不一定是安全的,因此到了jdk1.5之后,sun具体了volatile关键字,当然,或多或少影响点了性能,但是我们可以忽略不计。(参考Android源码设计模式一书)

再看最里面的if语句中,调用build()方法


    public Picasso build() {
      Context context = this.context;

      if (downloader == null) {
        downloader = Utils.createDefaultDownloader(context);
      }
      if (cache == null) {
        cache = new LruCache(context);
      }
      if (service == null) {
        service = new PicassoExecutorService();
      }
      if (transformer == null) {
        transformer = RequestTransformer.IDENTITY;
      }

      Stats stats = new Stats(cache);

      Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);

      return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
          defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
    }
  }


首先,如果我们在build的时候什么操作都没有,那么Picasso默认会帮助我们实现下载器、缓存、线城池、转换器、stats、和分发器这些类。
我们先看下载器:


    static Downloader createDefaultDownloader(Context context) {
    if (SDK_INT >= GINGERBREAD) {
      try {
        Class.forName("okhttp3.OkHttpClient");
        return OkHttp3DownloaderCreator.create(context);
      } catch (ClassNotFoundException ignored) {
      }
      try {
        Class.forName("com.squareup.okhttp.OkHttpClient");
        return OkHttpDownloaderCreator.create(context);
      } catch (ClassNotFoundException ignored) {
      }
    }
    return new UrlConnectionDownloader(context);
  }

由源码可以看出,Picasso默认给我们两种下载方式,okhttp和httpurlconnection,优先选择okhttp。

缓存:由名字可以看出,使用的是LruCache,主要是对java中的LinkedHashMap的使用。
线城池:线程池使用的是ThreadPoolExecutor,它是线程池的具体实现。

接着调用load方法,


    public RequestCreator load(@Nullable Uri uri) {
        return new RequestCreator(this, uri, 0);
      }

创建RequestCreator对象

into()方法:


    public void into(ImageView target) {
        into(target, null);
    }

      public void into(ImageView target, Callback callback) {
    long started = System.nanoTime();
    checkMain();

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

    if (!data.hasImage()) {
      picasso.cancelRequest(target);
      if (setPlaceholder) {
        setPlaceholder(target, getPlaceholderDrawable());
      }
      return;
    }

    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 || target.isLayoutRequested()) {
        if (setPlaceholder) {
          setPlaceholder(target, getPlaceholderDrawable());
        }
        picasso.defer(target, new DeferredRequestCreator(this, target, callback));
        return;
      }
      data.resize(width, height);
    }

    Request request = createRequest(started);
    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);
        }
        if (callback != null) {
          callback.onSuccess();
        }
        return;
      }
    }

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

    Action action =
        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
            errorDrawable, requestKey, tag, callback, noFade);

    picasso.enqueueAndSubmit(action);
  }

    boolean hasImage() {
          return uri != null || resourceId != 0;
        }

首先,检查当前线程是否是在主线程,如果不是,就报错.然后判断url是否为null,如果为null,就设置placeHolder并且返回,这里的placeHolder我们可以自定义。接着是对图片的一些处理。
然后就是创建Request对象,判断是否从内存缓存中读取,由于我们是第一次,所以if不成立。
再然后通过request构建Action对象,


        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 target = this.target.get();
        if (target == null) {
          return;
        }

        Context context = picasso.context;
        boolean indicatorsEnabled = picasso.indicatorsEnabled;
        PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);

        if (callback != null) {
          callback.onSuccess();
        }
      }

      @Override public void error() {
        ImageView target = this.target.get();
        if (target == null) {
          return;
        }
        Drawable placeholder = target.getDrawable();
        if (placeholder instanceof AnimationDrawable) {
          ((AnimationDrawable) placeholder).stop();
        }
        if (errorResId != 0) {
          target.setImageResource(errorResId);
        } else if (errorDrawable != null) {
          target.setImageDrawable(errorDrawable);
        }

        if (callback != null) {
          callback.onError();
        }
      }

      @Override void cancel() {
        super.cancel();
        if (callback != null) {
          callback = null;
        }
      }
    }

由Action的源码可以看出,该类的最重要的两个方法complete()和error()两个方法,最后,调用Picasso的enqueueAndSubmit方法。


    void enqueueAndSubmit(Action action) {
    Object target = action.getTarget();
    if (target != null && targetToAction.get(target) != action) {
      // This will also check we are on the main thread.
      cancelExistingRequest(target);
      targetToAction.put(target, action);
    }
    submit(action);
  }

     void submit(Action action) {
        dispatcher.dispatchSubmit(action);
      }

再看看Dispatcher的方法


    void dispatchSubmit(Action action) {
    handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
     }

发送一个请求提交的消息


    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) {
    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;
    }

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

    hunter = forRequest(action.getPicasso(), this, cache, stats, action);
    hunter.future = service.submit(hunter);
    hunterMap.put(action.getKey(), hunter);
    if (dismissFailed) {
      failedActions.remove(action.getTarget());
    }

    if (action.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
    }
  }

这里出现了一个新的类BitmapHunter,从源码可以看出它是一个任务对象


    class BitmapHunter implements Runnable 

既然它是一个任务对象,我们看它的run()方法

    ...
    updateThreadName(data);

      if (picasso.loggingEnabled) {
        log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
      }

      result = hunt();

      if (result == null) {
        dispatcher.dispatchFailed(this);
      } else {
        dispatcher.dispatchComplete(this);
      }
    ....

这里的hunt()方法比较重要,主要是这个方法完成图片的获取


     Bitmap hunt() throws IOException {
    Bitmap bitmap = null;

    if (shouldReadFromMemoryCache(memoryPolicy)) {
      bitmap = cache.get(key);
      if (bitmap != null) {
        stats.dispatchCacheHit();
        loadedFrom = MEMORY;
        if (picasso.loggingEnabled) {
          log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
        }
        return bitmap;
      }
    }

    data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
    RequestHandler.Result result = requestHandler.load(data, networkPolicy);
    if (result != null) {
      loadedFrom = result.getLoadedFrom();
      exifOrientation = result.getExifOrientation();
      bitmap = result.getBitmap();

      // If there was no Bitmap then we need to decode it from the stream.
      if (bitmap == null) {
        InputStream is = result.getStream();
        try {
          bitmap = decodeStream(is, data);
        } finally {
          Utils.closeQuietly(is);
        }
      }
    }

    if (bitmap != null) {
      if (picasso.loggingEnabled) {
        log(OWNER_HUNTER, VERB_DECODED, data.logId());
      }
      stats.dispatchBitmapDecoded(bitmap);
      if (data.needsTransformation() || exifOrientation != 0) {
        synchronized (DECODE_LOCK) {
          if (data.needsMatrixTransform() || exifOrientation != 0) {
            bitmap = transformResult(data, bitmap, exifOrientation);
            if (picasso.loggingEnabled) {
              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
            }
          }
          if (data.hasCustomTransformations()) {
            bitmap = applyCustomTransformations(data.transformations, bitmap);
            if (picasso.loggingEnabled) {
              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
            }
          }
        }
        if (bitmap != null) {
          stats.dispatchBitmapTransformed(bitmap);
        }
      }
    }

    return bitmap;
  }

假设我们第一次加载图片,自然是从网络获取,这里的requestHandler自然也就是NetworkRequestHandler。


    public Result load(Request request, int networkPolicy) throws IOException {
    Response response = downloader.load(request.uri, request.networkPolicy);
    if (response == null) {
      return null;
    }

    Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;

    Bitmap bitmap = response.getBitmap();
    if (bitmap != null) {
      return new Result(bitmap, loadedFrom);
    }

    InputStream is = response.getInputStream();
    if (is == null) {
      return null;
    }
    // Sometimes response content length is zero when requests are being replayed. Haven't found
    // root cause to this but retrying the request seems safe to do so.
    if (loadedFrom == DISK && response.getContentLength() == 0) {
      Utils.closeQuietly(is);
      throw new ContentLengthException("Received response with 0 content-length header.");
    }
    if (loadedFrom == NETWORK && response.getContentLength() > 0) {
      stats.dispatchDownloadFinished(response.getContentLength());
    }
    return new Result(is, loadedFrom);
  }

这个load方法就是通过调用下载器来获取流对象,然后将其封装成Result对象。而我们查看Result的源码结构上可以看出它是RequestHandler的内部类,内容上看也是对Bitmap的一个简单封装。
再回到Dispatcher的performSubmit()方法,其中有一行比较重要的代码


    hunter.future = service.submit(hunter);

这行代码就开始执行上面的run方法了。

由上面的run方法的hunt可知,最后又调用了dispatcher.dispatchComplete(this);


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

    case HUNTER_COMPLETE: {
          BitmapHunter hunter = (BitmapHunter) msg.obj;
          dispatcher.performComplete(hunter);
          break;
        }

再然后


    private void batch(BitmapHunter hunter) {
    if (hunter.isCancelled()) {
      return;
    }
    batch.add(hunter);
    if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
      handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
    }
     }

    case HUNTER_DELAY_NEXT_BATCH: {
      dispatcher.performBatchComplete();
      break;
    }

    void performBatchComplete() {
        List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
        batch.clear();
        mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
        logBatch(copy);
      }

这个方法又发送消息,但是这个mainThreadHandler是哪个?Handler呢?其实由名字就可以看出来,这是主线程的Handler,它是由当创建Dispatcher时通过构造函数传递过来的,因此,我们找到创建Dispatcher的地方,其实也就是在Picasso这个类的内部类的build()方法,


    Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);

我们看handleMessage()方法


    case HUNTER_BATCH_COMPLETE: {
          @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
          //noinspection ForLoopReplaceableByForEach
          for (int i = 0, n = batch.size(); i < n; i++) {
            BitmapHunter hunter = batch.get(i);
            hunter.picasso.complete(hunter);
          }
          break;
        }

可以看出,依次取出BitmapHunter对象,调用complete方法,


    void complete(BitmapHunter hunter) {
    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();

    if (single != null) {
      deliverAction(result, from, single);
    }

    if (hasMultiple) {
      //noinspection ForLoopReplaceableByForEach
      for (int i = 0, n = joined.size(); i < n; i++) {
        Action join = joined.get(i);
        deliverAction(result, from, join);
      }
    }

    if (listener != null && exception != null) {
      listener.onImageLoadFailed(this, uri, exception);
    }
  }

这里代码很多,最核心的是


    deliverAction(result, from, join);

    private void deliverAction(Bitmap result, LoadedFrom from, Action action) {
    if (action.isCancelled()) {
      return;
    }
    if (!action.willReplay()) {
      targetToAction.remove(action.getTarget());
    }
    if (result != null) {
      if (from == null) {
        throw new AssertionError("LoadedFrom cannot be null.");
      }
      action.complete(result, from);
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);
      }
    } else {
      action.error();
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_ERRORED, action.request.logId());
      }
    }
  }

这里,终于到了action的complete的方法,由于我们传递的是ImageView


     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 target = this.target.get();
    if (target == null) {
      return;
    }

    Context context = picasso.context;
    boolean indicatorsEnabled = picasso.indicatorsEnabled;
    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);

    if (callback != null) {
      callback.onSuccess();
    }
  }

这个方法最核心的是


     PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);

    static void setBitmap(ImageView target, Context context, Bitmap bitmap,
      Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
    Drawable placeholder = target.getDrawable();
    if (placeholder instanceof AnimationDrawable) {
      ((AnimationDrawable) placeholder).stop();
    }
    PicassoDrawable drawable =
        new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
    target.setImageDrawable(drawable);
     }

到了这里,我们终于看到了setImageDrawable()这个方法,总算完成了图片的显示。
OK,这篇博客就到这里了,就图片的显示流程做了个简单分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值