Picasso

项目中之前一直是使用ImageLoader加载图片,使用起来比较繁琐,加载图片的速度也比较慢,而且这个框架没有人维护了,所以改用Picasso加载图片。(缓存)(解决主要的问题:加载大量图片导致的卡顿)(在宏观层面上,用图去表达会更好)

Picasso模块集合.png

我们看一下它的调用方式:

        Picasso.with(context)
                .load(imageUrl)
                .into(imageView);

很简单,设置imageView、地址 就可以从网络中加载一张图片展示到ImageView中了(大多数图片框架都可以通过一句代码实现图片获取和显示)。

接着看Picasso类的代码

/**
 * Image downloading, transformation, and caching manager.
 * <p>
 * Use {@link #with(android.content.Context)} for the global singleton instance or construct your
 * own instance with {@link Builder}.
 */
public class Picasso {
}

开头讲明了Picasso的作用,Image downloading, transformation, and caching manager.
并提示我们使用单例调用或自己构建实例。
如: Picasso.with(context),这就是获取Picasso的单例对象。

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

接下来,我们分析主要的两个方法,load() 和into()。

Load():

public RequestCreator load(Uri uri) {
    return new RequestCreator(this, uri, 0);
}
public RequestCreator load(String path) {}
public RequestCreator load(File file) {}
public RequestCreator load(int resourceId) {}

很明显支持加载不同路径图片。
主要看这个:new RequestCreator(this, uri, 0);
一个请求构建类。

  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;
    this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
  }

在Builder的构造函数中:将值传递进去了。

  Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {
      this.uri = uri;
      this.resourceId = resourceId;
      this.config = bitmapConfig;
    }

Into():


  /**
   * Asynchronously fulfills the request into the specified {@link ImageView} and invokes the
   * target {@link Callback} if it's not {@code null}.
   * <p>
   * <em>Note:</em> The {@link Callback} param is a strong reference and will prevent your
   * {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected. If
   * you use this method, it is <b>strongly</b> recommended you invoke an adjacent
   * {@link Picasso#cancelRequest(android.widget.ImageView)} call to prevent temporary leaking.
   */
  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()) {//uri为null进入
      picasso.cancelRequest(target);
      if (setPlaceholder) {//为ImageView设置图片占位符
        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) {
        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);//使用requestKey可以从缓存中获取图片

    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());
    }
    //创建ImageViewAction
    Action action =
        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
            errorDrawable, requestKey, tag, callback, noFade);

    picasso.enqueueAndSubmit(action);
  }

接着调用: picasso.enqueueAndSubmit(action);

  void enqueueAndSubmit(Action action) {
    Object target = action.getTarget();//在Map中每一个action的target不一样。
    if (target != null && targetToAction.get(target) != action) {
      // This will also check we are on the main thread.
      cancelExistingRequest(target);
      targetToAction.put(target, action);//键值对保存Action的信息。
    }
    submit(action);
  }
  //然后将保含图片信息的Action提交给dispatcher。
  void submit(Action action) {
    dispatcher.dispatchSubmit(action);
  }
//构造函数  
Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
      Downloader downloader, Cache cache, Stats stats) {}

  void dispatchSubmit(Action action) {
    handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
  }
  void dispatchCancel(Action action) {}
  void dispatchPauseTag(Object tag) {}
  void dispatchResumeTag(Object tag) {}
  void dispatchComplete(BitmapHunter hunter) {}
  void dispatchRetry(BitmapHunter hunter) {}
  void dispatchFailed(BitmapHunter hunter) {}
  void dispatchNetworkStateChange(NetworkInfo info) {}
  void dispatchAirplaneModeChange(boolean airplaneMode) {}
  void performSubmit(Action action, boolean dismissFailed) {}
  void performCancel(Action action) {}
  void performPauseTag(Object tag) {}
  void performResumeTag(Object tag) {}
  void performRetry(BitmapHunter hunter) {}
  void performComplete(BitmapHunter hunter) {}
  void performBatchComplete() {}
  void performError(BitmapHunter hunter, boolean willReplay){}

Dispatcher 负责分发和处理 Action,包括提交、暂停、继续、取消、网络状态变化、重试等等。
需要注意的一点是: Dispatcher 中的static class NetworkBroadcastReceiver extends BroadcastReceiver {}中注册了ACTION_AIRPLANE_MODE_CHANGED和CONNECTIVITY_ACTION两个广播,支持飞行模式、并发线程数根据网络类型而变
手机切换到飞行模式或网络类型变换时会自动调整线程池最大并发数。

接着DispatcherHandler 调用performSubmit。

 void performSubmit(Action action, boolean dismissFailed) {
        ...
        BitmapHunter hunter = (BitmapHunter)this.hunterMap.get(action.getKey());//一个Runnable
        ...
        hunter = BitmapHunter.forRequest(action.getPicasso(), this, this.cache, this.stats, action);
        hunter.future = this.service.submit(hunter);//将Runnable提交到service线程池中
        ...
    }

将任务提交后,在Picasso类的Handler中获取到回调信息。

static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
    @Override public void handleMessage(Message msg) {
      switch (msg.what) {
        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;
        }
//然后
  void complete(BitmapHunter hunter) {
    if (single != null) {
      deliverAction(result, from, single);
    }
//然后
  private void deliverAction(Bitmap result, LoadedFrom from, Action action) {
      action.complete(result, from);
  }
  //在ImageAction中:
    @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
    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);//通过PicassoDrawable展示图片

Picasso加载图片流程:Picasso 收到加载及显示图片的任务,然后从MemoryCache中获取图片,如果没有缓存则创建 ImageViewAction并将它交给 Dispatcher,Dispatcher 分发任务到具体 Handler中,然后通过线程池执行BitmapHunter(Runnable)任务, 最后通过Handler(数据获取接口) 获取图片,最终回调到Picasso类中的handler中处理,图片获取成功后通过 PicassoDrawable 显示到 Target 中。

Picasso加载网络图片流程图.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容