Android:Picasso

创建

建议使用builder创建Picasso

Picasso.Builder builder = new Picasso.Builder(this);
Picasso.setSingletonInstance(builder.build());

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

通过构造方法得知有几个参数值得关注:
downloader、cache、service、transformer

downloader

通过反射判断使用OkHttpLoaderCreator或者UrlConnectionDownloader,通过OkHttpLoaderCreator建立的downloader还会通过getCacheDir方法获取缓存路径建立文件名为“picasso-cache”的Picasso的缓存文件。其中OkHttpDownloader为通过OkHttp请求来下载网络图片的类,具体内容体现在后面讲述的load方法中。

cache

缓存默认使用LRU算法,即least-recently used,近期最少使用算法。首先在LruCache的构造函数中计算出一个合理的大小(15%、1/7),作为缓存的最大空间。

service

PicassoExecutorService实现Picasso线程池,构造函数中实例化工作队列和线程工厂。

transformer

RequestTransformer,请求在被执行前经过一层转换。

stat

通过Stat标记缓存的状态(命中数、未命中数、总大小、平均大小、下载次数等)

dispatcher

分发处理事件,如图片加载完成、请求提交、请求取消等。

load

public RequestCreator load(Picasso picasso, Uri uri, int resourceId) {
    
    
    if (picasso.shutdown) {
    
    
      throw new IllegalStateException(
          "Picasso instance already shut down. Cannot  submit new requests.");
    }
    this.picasso = picasso;
    //消息都存在RequestCreator.data中,最终返回对象类型为RequestCreator
    this.data = new Request.Builder(uri, resourceId,  
      picasso.defaultBitmapConfig);
  }

最终返回对象类型为RequestCreator,可以理解为一个构造器,我们可以在后面添加占位符等,这些参数会暂存在RequestCreator的成员变量内部,等到用户调用into(imageview)方法时,再把所有参数填充到Request

into

public void into(ImageView target, Callback callback) {
    
    
  long started = System.nanoTime();
  checkMain();  //确保在主线程调用该方法
  if (target == null) {
    
    
    throw new IllegalArgumentException("Target must not be null.");
  }
  //如果图片的uri为null,则取消请求,然后设置占位图
  if (!data.hasImage()) {
    
    
    picasso.cancelRequest(target);
    if (setPlaceholder) {
    
    
      setPlaceholder(target, getPlaceholderDrawable());
    }
    return;
  }
  //如果调用了RequestCreator#fit()方法,那么deferred会被设置为true
  //这是因为fit()方法需要适应ImageView的大小,必须等到ImageView的layout过程完毕才能fit()
  //因此,这里实际上是推迟了图片的加载过程,即Picasso#defer()
  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);
  }

这里切开分析,前面这里进行了几个确认:是否在主线程、是否设置了targetview,是否设置了uri、是否同时设置了fix和resize,是否设置了占位符,fix限制targetView为ImagerView,其内部会完成测量和裁剪在这里插入代码片

  Request request = createRequest(started); //根据RequestCreator的参数来创建一个Request
  String requestKey = createKey(request);   //创建与该Request对应的一个Key
  //如果内存缓存可用,那么直接从内存缓存获取Request对应的Bitmap,并取消请求
  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 action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy,errorResId,errorDrawable, requestKey, tag, callback, noFade);
  picasso.enqueueAndSubmit(action); //排队,等待调度
}

这里主要是创建Request,装入RequestBuilder的成员变量信息
先检查内存缓存是否可用,如果可用就直接加载,并取消request
若不可用才创建action进入队伍

Dispatcher.submit

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

向dispatcher所在线程发送消息

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

performSubmit

//代码清单4-1:Dispatcher#performSubmit(action)
void performSubmit(Action action, boolean dismissFailed) {
    
    
  //省略...
  hunter = forRequest(action.getPicasso(), this, cache, stats, action);
  hunter.future = service.submit(hunter);
  hunterMap.put(action.getKey(), hunter); 
}

//BitmapHunter#forRequest
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
    Action action) {
    
    
  Request request = action.getRequest();
  List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
  //找到一个可以处理该Request的RequestHandler
  for (int i = 0, count = requestHandlers.size(); i < count; i++) {
    
    
    RequestHandler requestHandler = requestHandlers.get(i);
    if (requestHandler.canHandleRequest(request)) {
    
    
      return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
    }
  }
  return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
}

从上面的代码可以看出,先是生成了一个BitmapHunter,这个类的作用顾名思义,就是获取Bitmap,它是一个Runnable,它内部根据Request的不同类型来确定不同的获取方法(实际上是RequestHandler在起作用)。

紧接着,调用了service.submit(hunter)方法,这里的service实际上就是PicassoExecutorService线程池,将BitmapHunter这个runnable投递进了线程池,如果线程池有空闲的线程那么就会执行这个runnable,否则阻塞等待。最终,如果runnable获得执行的机会,它的run()方法会被调用。

BitmapHunter run

//代码清单5:BitmapHunter#run
@Override public void run() {
    
    
  try {
    
    
    updateThreadName(data);
    //获取result
    result = hunt();
    if (result == null) {
    
    
      //如果加载失败,则分发失败事件
      dispatcher.dispatchFailed(this);
    } else {
    
    
      //如果加载成功,则分发成功事件
      dispatcher.dispatchComplete(this);
    }
  } 
  //省略异常状态的处理...
}

Bitmap hunt

//代码清单5-1:BitmapHunter#hunt()
Bitmap hunt() throws IOException {
    
    
  Bitmap bitmap = null;
  //从内存缓存读取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解析图片请求
  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.
    // 如果bitmap为空,那么从stream读取字节流解析成bitmap
    if (bitmap == null) {
    
    
      Source source = result.getSource();
      try {
    
    
        bitmap = decodeStream(source, data);
      } finally {
    
    
        try {
    
    
          //noinspection ConstantConditions If bitmap is null then source is guranteed non-null.
          source.close();
        } catch (IOException ignored) {
    
    
        }
      }
    }
  }
  if (bitmap != null) {
    
    
    //省略部分代码...
    //对Bitmap进行转换操作,Transformation是一个自定义的转换操作
    if (data.needsTransformation() || exifOrientation != 0) {
    
    
      synchronized (DECODE_LOCK) {
    
    
        if (data.needsMatrixTransform() || exifOrientation != 0) {
    
    
          bitmap = transformResult(data, bitmap, exifOrientation);      
        }
        if (data.hasCustomTransformations()) {
    
    
          bitmap = applyCustomTransformations(data.transformations, bitmap);          
        }
      }    
    }
  }
  return bitmap;
}

此处就是进行转换,先前设置的参数作用的地方,以及自己设置的transformation
此方法返回bitmap,并调用dispatcher.dispatchComplete(this),从线程池里面的线程切换为dispatcher线程

performComplete(BitmapHunter)

  void performComplete(BitmapHunter hunter) {
    
    
    //如果条件允许,那么把该bitmap缓存到内存
    if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
    
    
      cache.set(hunter.getKey(), hunter.getResult());
    }
    hunterMap.remove(hunter.getKey());  //从map移除这个已完成的hunter
    batch(hunter);  //进行批处理  
  }
  
  private void batch(BitmapHunter hunter) {
    
    
    if (hunter.isCancelled()) {
    
    
      return;
    }
    if (hunter.result != null) {
    
    
      hunter.result.prepareToDraw();
    }
    batch.add(hunter);  //添加到batch列表内
    if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
    
    
      handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
    }
  }

从上面源码可以看出,该hunter会被添加到一个batch的列表内,同时延迟发送一个HUNTER_DELAY_NEXT_BATCH消息,这意味着,第一个hunter完成后,会被添加到batch列表,然后延迟200ms发送batch消息。此时如果有别的hunter到达,也会被一一添加到batch列表,直到一开始的batch消息得到处理。这里利用了批处理的思想,在200ms的等待时间内,会暂存多个hunter请求,时间到了之后便切换到主线程进行UI的显示,这样就不用频繁地进行线程切换,可以提升UI显示的流畅性。

performBatchComplete

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

这里的mainThreadHandler是持有主线程Looper的handler,它发送的消息都会在主线程得到处理,在dispatcher初始化传入了主线程的looper

complete

//代码清单8-1:Picasso#complete
void complete(BitmapHunter hunter) {
    
    
    //获取hunter所含有的Action
    Action single = hunter.getAction();           
    //hunter可能对应多个Action,对同一图片的同一操作的多个请求会保存在一个hunter内
    //避免不必要的重复加载步骤。
    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, exception);
    }
    if (hasMultiple) {
    
    
      //noinspection ForLoopReplaceableByForEach
      for (int i = 0, n = joined.size(); i < n; i++) {
    
    
        Action join = joined.get(i);
        deliverAction(result, from, join, exception);
      }
    }
    if (listener != null && exception != null) {
    
    
      listener.onImageLoadFailed(this, uri, exception);
    }
  }

取出action,调用deliverAction

deliverAction、complete

  //代码清单8-2:Picasso#deliverAction
  private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
    
    
    //省略...
    if (result != null) {
    
    
      action.complete(result, from);
    } else {
    
    
      action.error(e);    
  }


@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();
    }
  }

PicassoDrawable继承自BitmapDrawable,Picasso是以Drawable的形式把图片设置进ImageView的,通过这样的形式,Picasso可以最后在图片上添加一些信息。比如,开启了Debug模式后,所加载的图片的右下角会有不同颜色的角标来表示图片的来源(网络、内存或磁盘),这个功能的实现就是借助于BitmapDrawable.draw方法在画布上添加额外的信息。
最后调用callback.onSuccess

setBitmap

  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();
    }
    PicassoDrawable drawable =
        new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
    target.setImageDrawable(drawable); //最后的最后,把drawable设置进imageview上
  }

猜你喜欢

转载自blog.csdn.net/weixin_51109304/article/details/131332551