Picasso 图片加载

1,216 阅读14分钟

1、简介

Picasso是由Square公司推出的一个功能强大的Android图片加载和缓存库;有人经常拿它和glide相比,而且glide的使用频率更高;这时候肯定得有但是啊,下面来看看2.71828版本有哪些特点

  1. 默认采用三级缓存LRU策略
  2. 可以采取不同的图片格式进行内存缓存、并且按照图片期望大小来缓存
  3. 磁盘缓存采用okHttp的缓存策略,全尺寸缓存;如果不采用默认实现需要自己实现
  4. 可以取消暂停下载

和glide比较,明显的差异性,主要在以下方面

  1. picasso不能自动处理生命周期,但是却减少了glide因为生命周期而产生的crash;
  2. 内存溢出,picasso提供了图片格式选择,而且内存缓存期望大小图片,和glide并没有实质区别
  3. glide可以加载gif等, picasso默认实现不支持;但这种场景多吗?性能好吗?
  4. 包体积大小,picasso确实有优势,但其需要依赖okhttp库,也可以自定义实现
  5. 使用方便,glide确实使用的方便很不错;但是进行内存优化,同样都是门槛有点(不过我一直认为,图片按需请求、加载才是省流性能优化的王道,但是基本没有见服务器端会给做。。。)
  6. picasso默认实现不支持请求drawable的图;Drawable是一种可绘制在画布的图形,bitmap是jpg、png等格式的图片;drawable绘制更好,未看glide的源码,因为资源是位图,如果请求drawable,肯定是要转换的;

从现在这个版本来看,实质上没有多大区别了,但是glide已经抢先占领市场了,picasso只能在后面吃灰了,不过这只是作为吃瓜群众看看而已;使用选择,我是建议,如果使用了Okhttp库,picasso会是个不错的选择

2、基础知识

涉及的基础知识,主要介绍图片和缓存内容(内容均以Picasso中实现为模板)

  1. 图片的网络请求
  2. 图片解析
  3. 采样率压缩
  4. 图片平移缩放
  5. LRU缓存

2.1 图片网络请求

使用Okhttp请求时,不需要做额外的请求头处理,只需要添加缓存策略即可;okhttp会自动添加请求类型等信息;如果需要了解请求报文具体添加哪些信息,可以查看OkHttp手把手教你网络编程

2.2 图片解析

涉及两个类:BitmapFactory, BitmapFactory.Options;BitmapFactory中有一些解析图片方法,从流、文件、byte数组、资源id中;Options主要对解析做了以下限制

  1. inJustDecodeBounds:如果为true,不必全部解析图片内容,只需要解析图片相关信息即可
  2. inSampleSize:解析图片是,对图片进行缩放倍数
  3. inPreferredConfig:图片解析时预期格式,如果不能解析,则按照ARGB_8888格式
  4. inPurgeable:系统内存不足时是否可以回收这个bitmap,有点类似软引用,但是实际在5.0以后这两个属性已经被忽略
  5. inInputShareable:inPurgeable为true时才有效,且5.0以后被忽略;表示多个引用时是否深copy

2.3 采样率压缩

  1. 设置inJustDecodeBounds=true解析图片,解析图片实际大小
  2. 求出缩放因子,从 预期宽度/实际宽度 和 预期高度/实际高度中取
  3. inJustDecodeBounds=false解析图片

其实图片有时候还需要进行压缩,经典的压缩写法,利用ByteArray输入输出流和bitmap的类方法compress

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            wallpaperBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            InputStream inputStream = new ByteArrayInputStream(baos.toByteArray());

compress中参数介绍:

  1. android中有三种格式,jpeg,png,webp
  2. 压缩质量,从0-100, 100表示无损压缩
  3. 输出流

使用compress压缩,不能改变图片的宽高,也就是改变不了内存中大小,只是可以改变文件中大小

2.4 图片平移缩放

利用Bitmap的静态方法处理

createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)

Matrix中包含缩放和平移操作

2.5 缓存逻辑

  1. 请求对象,使用RequestWeakReference来进行弱引用缓存,使用ReferenceQueue来清除无效的关联动作,以减少无效的对象大量堆积

  2. 内存缓存采用sdk中LruCache进行处理,磁盘缓存采用DiskLruCache缓存 LruCache:是通过HashMap结构+双链表结构,hashMap用来增删改查,双链表顺序,标记最近使用顺序

  3. 磁盘缓存,使用DiskLruCache,其存储一个journal操作日志文件,和一些以名字为key的文件,这些文件才是实际缓存;其中记录有四种类型DIRTY,CLEAN,REMOVE, READ;这里记录会临时存在LruCache中,用来达到阈值时进行移除操作;

    • 读操作时,写入READ记录
    • 写操作首先写入DIRTY记录,如果后面未有CLEAN记录,则是无效记录
    • 删除写入REMOVE;
    • 根据操作,LruCache增删改查并调整最近使用,同时保证磁盘读写,磁盘写成功,写入CLEAN记录;

3、源码分析

最常见使用方式:

Picasso.get()
        .load(url)
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .resizeDimen(100, 100)
        .centerInside()
        .tag(context)
        .transform(new GrayscaleTransformation(Picasso.get()))
        .memoryPolicy(MemoryPolicy.NO_CACHE)
        .networkPolicy(NetworkPolicy.OFFLINE)
        .into(image);

这行代码进行了picasso库中常用的一些操作:设置网络缓存参数,默认图片,请求错误图片,期望大小,设置请求tag方便取消暂停恢复等操作

3.1 Picasso类

Picasso对象应该是单例的,包含处理必要元素以及可以设置一些通用设置;

  • 默认通用配置,直接使用静态方法get
  • 修改默认配置使用Picasso.Builder来处理,然后使用Picasso静态方法setSingletonInstance设置实例;使用时依然使用get方法即可

get静态方法很巧妙,利用了ContentProvider在应用创建时就会初始化,而获取应用上下文

    private final Context context;
    private Downloader downloader;
    private ExecutorService service;
    private Cache cache;
    private Listener listener;
    private RequestTransformer transformer;
    private List<RequestHandler> requestHandlers;
    private Bitmap.Config defaultBitmapConfig;

    private boolean indicatorsEnabled;
    private boolean loggingEnabled;

Picasso利用构建者模式,可以设置以上通用设置:

  1. 上下文环境
  2. 网络下载器,默认okhttp下载
  3. 执行线程,默认PicassoExecutorService(可以根据网络情况来改变线程池个数;仅有核心线程的线程池)
  4. 请求对象转换,默认不做任何处理
  5. 各种图片资源实际查找加载者:默认已实现文件、多媒体、Assets资源、http资源、联系人图片资源、contentProvider提供资源
  6. 内存缓存处理,默认使用LruCache
  7. 失败监听
  8. 图片解析时默认选项
  9. 是否显示log、指示器

普通处理者,基本不用个性化定制通用配置;特别注意,如果定制下载器,要注意实现磁盘缓存

必要元素:

  private final CleanupThread cleanupThread;
  final Dispatcher dispatcher;
  final Stats stats;
  final Map<Object, Action> targetToAction;
  final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;
  final ReferenceQueue<Object> referenceQueue;
  • 请求填充对象的弱引用队列,和清理线程;(into方法传入参数,视为填充对象)
  • 请求处理消息调度器
  • 库中图片请求缓存等情况
  • 填充对象--和请求动作信息集合
  • ImageView为填充对象时,fit操作处理集合(fit操作的,必须是调用into方法,且是ImageView填充,否则会报错,请谨记使用)

Picasso 主要操作如下:方法如何实现,在后续会慢慢讲

  1. 暂停、取消、恢复 tag对应的下载:cancelRequest,cancelTag,pauseTag,resumeTag
  2. 清理某一些图片:invalidate
  3. 获取当前图片库对图片处理的情况:getSnapshot方法

3.2 RequestCreator类

如果Picasso中记录者通用配置,那么此类中的配置就是针对每个图片的请求了,同时也是请求处理启动的地方

可配置信息如下:

  private final Request.Builder data;
  private boolean noFade;
  private boolean deferred;
  private boolean setPlaceholder = true;
  private int placeholderResId;
  private int errorResId;
  private int memoryPolicy;
  private int networkPolicy;
  private Drawable placeholderDrawable;
  private Drawable errorDrawable;
  private Object tag;
  • 没有动画效果;图片加载完成之后,载入是无动画效果
  • 是否延时处理,延时处理会在ImageView宽高测量结束后,去获取图片,并裁剪
  • 是否设置过过默认图片
  • 请求失败图片
  • 内存策略
  • 网络请求策略
  • 请求tag标志
  • 请求到图片的旋转、平移、缩放、其它转换效果信息

同步请求开始

public Bitmap get() throws IOException {
    long started = System.nanoTime();
    checkNotMain();

    if (deferred) {
      throw new IllegalStateException("Fit cannot be used with get.");
    }
    if (!data.hasImage()) {
      return null;
    }

    Request finalData = createRequest(started);
    String key = createKey(finalData, new StringBuilder());

    Action action = new GetAction(picasso, finalData, memoryPolicy, networkPolicy, tag, key);
    return forRequest(picasso, picasso.dispatcher, picasso.cache, picasso.stats, action).hunt();
  }

Action记录请求的信息:包括请求结果的转换信息、网络策略、内存策略、tag、和按照request对象得到的key标志; forRequest方法,生成了请求处理图片的线程BitmapHunter对象,hunt() 方法可以得到图片

异步请求

fetch方法

public void fetch(@Nullable Callback callback) {
    long started = System.nanoTime();

    if (deferred) {
      throw new IllegalStateException("Fit cannot be used with fetch.");
    }
    if (data.hasImage()) {
      if (!data.hasPriority()) {
        data.priority(Priority.LOW);
      }

      Request request = createRequest(started);
      String key = createKey(request, new StringBuilder());

      if (shouldReadFromMemoryCache(memoryPolicy)) {
        Bitmap bitmap = picasso.quickMemoryCacheCheck(key);
        if (bitmap != null) {
          if (picasso.loggingEnabled) {
            log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
          }
          if (callback != null) {
            callback.onSuccess();
          }
          return;
        }
      }

      Action action =
          new FetchAction(picasso, request, memoryPolicy, networkPolicy, tag, key, callback);
      picasso.submit(action);
    }
  }

优先从缓存中获取,如果没有,则生成Action,并通过Picasso对象提交任务,进而通过Dispatcher来调度处理

into方法也是大同小异,只不过生成的Action不同,提交任务时需要在Picasso中放置集合中,以做到同一个taget,均请求最新的要求;

3.3 Dispatcher类

利用handler消息,进行调度处理

  static final int REQUEST_SUBMIT = 1;
  static final int REQUEST_CANCEL = 2;
  static final int REQUEST_GCED = 3;
  static final int HUNTER_COMPLETE = 4;
  static final int HUNTER_RETRY = 5;
  static final int HUNTER_DECODE_FAILED = 6;
  static final int HUNTER_DELAY_NEXT_BATCH = 7;
  static final int HUNTER_BATCH_COMPLETE = 8;
  static final int NETWORK_STATE_CHANGE = 9;
  static final int AIRPLANE_MODE_CHANGE = 10;
  static final int TAG_PAUSE = 11;
  static final int TAG_RESUME = 12;
  static final int REQUEST_BATCH_RESUME = 13;

请求提交消息、请求取消消息、请求取消包括延时任务、请求成功、请求失败、请求重试、暂停、恢复、飞行模式、网络变化

3.3.1 请求提交消息;

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());
    }
  }
  • 如果在暂停动作中,则不进行处理
  • 如果key对应的执行BitmapHunter任务存在,动作依附到任务上,返回
  • 任务线程关闭,则返回
  • 生成任务实例,并加入任务集合中,并提交到线程池进行执行,失败动作集合去除此动作

3.3.2 取消消息

void performCancel(Action action) {
    String key = action.getKey();
    BitmapHunter hunter = hunterMap.get(key);
    if (hunter != null) {
      hunter.detach(action);
      if (hunter.cancel()) {
        hunterMap.remove(key);
        if (action.getPicasso().loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_CANCELED, action.getRequest().logId());
        }
      }
    }

    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");
    }
  }
  • 如果动作相关任务仍在运行,则取消依附,并停止任务
  • 暂停、失败动作集合移除此动作

3.3.3 弱引用对象无效清除Action消息

void cancelExistingRequest(Object target) {
    checkMain();
    Action action = targetToAction.remove(target);
    if (action != null) {
      action.cancel();
      dispatcher.dispatchCancel(action);
    }
    if (target instanceof ImageView) {
      ImageView targetImageView = (ImageView) target;
      DeferredRequestCreator deferredRequestCreator =
          targetToDeferredRequestCreator.remove(targetImageView);
      if (deferredRequestCreator != null) {
        deferredRequestCreator.cancel();
      }
    }
  }

进行取消处理,如果是ImageView的图片请求,去掉延迟请求任务

3.3.4 请求成功消息

void performComplete(BitmapHunter hunter) {
    if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
      cache.set(hunter.getKey(), hunter.getResult());
    }
    hunterMap.remove(hunter.getKey());
    batch(hunter);
    if (hunter.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
    }
  }
  
  private void batch(BitmapHunter hunter) {
    if (hunter.isCancelled()) {
      return;
    }
    if (hunter.result != null) {
      hunter.result.prepareToDraw();
    }
    batch.add(hunter);
    if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
      handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
    }
  }
  • 如果需要进行内存缓存,则进行缓存处理
  • 移除任务;并加入结果队列中,进行延时处理

3.3.5 重试消息

void performRetry(BitmapHunter hunter) {
    if (hunter.isCancelled()) return;

    if (service.isShutdown()) {
      performError(hunter, false);
      return;
    }

    NetworkInfo networkInfo = null;
    if (scansNetworkChanges) {
      ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
      networkInfo = connectivityManager.getActiveNetworkInfo();
    }

    if (hunter.shouldRetry(airplaneMode, networkInfo)) {
      if (hunter.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_RETRYING, getLogIdsForHunter(hunter));
      }
      if (hunter.getException() instanceof NetworkRequestHandler.ContentLengthException) {
        hunter.networkPolicy |= NetworkPolicy.NO_CACHE.index;
      }
      hunter.future = service.submit(hunter);
    } else {
      boolean willReplay = scansNetworkChanges && hunter.supportsReplay();
      performError(hunter, willReplay);
      if (willReplay) {
        markForReplay(hunter);
      }
    }
  }
  • 检查任务、线程池状态
  • 任务是否可以重试,可以重试,则重新在线程池中提交任务(网络请求,默认可以重试两次)
  • 如果任务不可以重试,且当前网络发生变化时可重试,则把任务放入failed任务队列中;等待网络变化时,重新处理

3.3.6 失败消息

void performError(BitmapHunter hunter, boolean willReplay) {
    if (hunter.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter),
          "for error" + (willReplay ? " (will replay)" : ""));
    }
    hunterMap.remove(hunter.getKey());
    batch(hunter);
  }

移除任务,并加入结果处理集合,进行延时处理

3.3.7 结果队列继续处理消息

copy已经请求有结果的任务,并清除结果,发送结果处理消息

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

3.3.8 结果处理消息

case HUNTER_BATCH_COMPLETE: {
          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;
        }

complete方法中最终会根据hunter任务结果,调用依附其的Action的complete方法或者error方法

3.3.9 网络变化消息

void performNetworkStateChange(NetworkInfo info) {
    if (service instanceof PicassoExecutorService) {
      ((PicassoExecutorService) service).adjustThreadCount(info);
    }
    if (info != null && info.isConnected()) {
      flushFailedActions();
    }
  }

如果是PicassoExecutorService,则会调节线程池中线程池个数;并重新处理failed任务集合中任务

3.3.10 飞行模式变化

  void performAirplaneModeChange(boolean airplaneMode) {
    this.airplaneMode = airplaneMode;
  }

3.3.11 暂停任务消息

void performPauseTag(Object tag) {
    // Trying to pause a tag that is already paused.
    if (!pausedTags.add(tag)) {
      return;
    }

    for (Iterator<BitmapHunter> it = hunterMap.values().iterator(); it.hasNext();) {
      BitmapHunter hunter = it.next();
      boolean loggingEnabled = hunter.getPicasso().loggingEnabled;

      Action single = hunter.getAction();
      List<Action> joined = hunter.getActions();
      boolean hasMultiple = joined != null && !joined.isEmpty();

      if (single == null && !hasMultiple) {
        continue;
      }

      if (single != null && single.getTag().equals(tag)) {
        hunter.detach(single);
        pausedActions.put(single.getTarget(), single);
        if (loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_PAUSED, single.request.logId(),
              "because tag '" + tag + "' was paused");
        }
      }

      if (hasMultiple) {
        for (int i = joined.size() - 1; i >= 0; i--) {
          Action action = joined.get(i);
          if (!action.getTag().equals(tag)) {
            continue;
          }

          hunter.detach(action);
          pausedActions.put(action.getTarget(), action);
          if (loggingEnabled) {
            log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
                "because tag '" + tag + "' was paused");
          }
        }
      }

      if (hunter.cancel()) {
        it.remove();
        if (loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_CANCELED, getLogIdsForHunter(hunter), "all actions paused");
        }
      }
    }
  }
  • 标签、任务、动作,放入相应的暂停集合中
  • 动作脱离任务,取消任务

3.3.12 恢复任务

void resumeAction(Action action) {
    Bitmap bitmap = null;
    if (shouldReadFromMemoryCache(action.memoryPolicy)) {
      bitmap = quickMemoryCacheCheck(action.getKey());
    }

    if (bitmap != null) {
      deliverAction(bitmap, MEMORY, action, null);
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + MEMORY);
      }
    } else {
      enqueueAndSubmit(action);
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_RESUMED, action.request.logId());
      }
    }
  }
  • 尝试从内存获取图片,获取成功,则调用Action进行成功处理
  • 否则,重新加入队列,并提交动作

3.4 请求动作Action

抽象类;存储了请求的信息;有如下抽象方法

  abstract void complete(Bitmap result, Picasso.LoadedFrom from);

  abstract void error(Exception e);

抽象方法包含了,对请求结果成功,失败的处理;有如下默认实现类:FetchAction、RemoteViewsAction、GetAction、ImageViewAction、TargetAction;RemoteViewsAction有通知栏和appwidget两种类别;

3.5 RequestHandler类

抽象类,主要有如下方法:

  public abstract boolean canHandleRequest(Request data);

  public abstract Result load(Request request, int networkPolicy) throws IOException;

  int getRetryCount() {
    return 0;
  }

  boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
    return false;
  }

  boolean supportsReplay() {
    return false;
  }
  • 两个抽象方法:是否处理请求,以及请求具体执行
  • 重试机制方法控制,优先级如下
    1. 默认失败重试次数为0
    2. 飞行模式和网络情况下是否可重试,默认不可以
    3. 是否在网络发生变化后,可以重试

实现类有:AssetRequestHandler,ContactsPhotoRequestHandler,ContentStreamRequestHandler,MediaStoreRequestHandler,NetworkRequestHandler,ResourceRequestHandler; 也即可以处理Asset资源,通讯录中联系人图片信息,内容提供者中图片、多媒体中图片、http网络、id资源;也可以自实现处理

3.6 BitmapHunter类

线程任务类,run方法,调用hunt方法获取图片,并进行结果处理

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

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

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

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

    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;
  }
  1. 缓存获取
  2. RequestHandler的load方法获取图片
  3. 图片进行变换,进行统计等操作,最后返回图片

类中存在attach、detach方法,也即是多个action可以共用同一个图片请求

3.7 ImageView的延迟处理

这个主要在DeferredRequestCreator类中,其实现了OnPreDrawListener, OnAttachStateChangeListener接口; 通过view的生命周期onViewAttachedToWindow --- onViewDetachedFromWindow 注册/解注OnPreDrawListener监听,在这个监听内,重新进行请求

4、小结

除了介绍中关于Picasso的特性外,还有如下特点,开发过程需要留意

  1. 同一时间下载图片个数存在限制(wifi 4个, 4G 3个, 3G 2个, 2G 1个,其它为默认3个)
  2. 同一时间,同一个填充对象,只能有一个请求,以最新请求为准
  3. 同一时间,同一个图片请求,只能有一个,多个请求Action对象共用请求结果
  4. 磁盘缓存借助Okhttp请求缓存处理;若自定义下载器,需要对接实现磁盘缓存
  5. error或者placeholder图片实现Animatable接口,具有动画效果
  6. 图片不是从内存加载成功后,有可能会有填充的alpha动销,这个属性由noFade决定
  7. 在onCreate中imageView加载期望大小的图片时,使用fit处理,可以延时在宽高测量成功后,获取并裁剪图片
  8. 暂停恢复操作,并不是继续处理下载,而是取消下载、重新获取的机制;感觉很鸡肋,也就是能够停止UI的操作部分
  9. 缩放图片操作,只有CenterCrop和CenterInside两种,缩放后,由于可能存在一条边并不一定和期望大小一致,所以可以进行靠上、下、左、右四个方向或者剧中放置

技术变化都很快,但基础技术、理论知识永远都是那些;作者希望在余后的生活中,对常用技术点进行基础知识分享;如果你觉得文章写的不错,请给与关注和点赞;如果文章存在错误,也请多多指教!