Picasso 源码分析

来源:互联网 发布:网络教育研究的资料 编辑:程序博客网 时间:2024/05/17 23:57

Picasso.with(context) 这个方法得到一个单例的Picasso类
这里写图片描述

public Picasso with(){//加入同步锁if (singleton == null) {      synchronized (Picasso.class) {        if (singleton == null) {          singleton = new Builder(context).build();        }      }}

构造方法中只是new 了一个Build类
with方法内部通过Builder模式创建Picasso实例,这样做的好处是简洁清晰,通常在构造器参数很多的时候使用。
build方法会最终创建Picasso实例:

 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;      }      return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,          defaultBitmapConfig, indicatorsEnabled, loggingEnabled);    }

此方法做了如下基本配置:

使用默认的缓存策略,内存缓存基于LruCache,磁盘缓存基于http缓存,HttpResponseCache 创建默认的下载器 创建默认的线程池(3个worker线程) 创建默认的Transformer,这个Transformer什么事情也不干,只负责转发请求 创建默认的监控器(Stats),用于统计缓存命中率、下载时长等等 创建默认的处理器集合,即RequestHandlers.它们分别会处理不同的加载请求
处理器集合的初始化在Picasso的构造器中:
这里写图片描述
Picasso构造器

 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);
//实例化各种成员变量 public Picasso build() { 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;      }

从命名就可以看出来,可以从网络、file、assert、contactsphoto等地方加载图片.

另,Picasso支持增加自己的处理器.

 **load()方法用于从不同地方加载图片,比如网络、resource、File等,该方法内部逻辑很简单,只是创建了一个RequestCreator

Picasso结构**

 public RequestCreator load(Uri uri) {    return new RequestCreator(this, uri, 0);  }
  **RequestCreator**从名字就可以知道这是一个封装请求的类,请求在Picasso中被抽象成Request。RequestCreator类提供了

诸如placeholder、tag、error、memoryPolicy、networkPolicy等方法.
由于可配置项太多,所以Request也使用了Builder模式:

 **RequestCreator构造器**
//data为Build类 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);  }
 那么可想而知into方法一定会去将Request创建,并丢到线程池或者分发器中执行。into方法有多种重载,因为Picasso不仅仅可以 将图片加载到ImageView上,还可以加载到Target或者RemoteView上.

这里选取imageView作为分析对象,该方法代码如下:

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或者resID      //如果没有设置当然取消请求      picasso.cancelRequest(target);      if (setPlaceholder) {        setPlaceholder(target, getPlaceholderDrawable());      }      return;    }    if (deferred) {//是否调用了fit(),如果是,代表需要将image调整为ImageView的大小      if (data.hasSize()) {//不能与resize一起用        throw new IllegalStateException(Fit cannot be used with resize.);      }      //既然要适应ImageView,肯定需要拿到ImageView大小      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 request = createRequest(started);    String requestKey = createKey(request);    if (shouldReadFromMemoryCache(memoryPolicy)) {//是否需要在缓存里面先查找      Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);      if (bitmap != null) {//cache hit        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;      }    }    //缓存未命中,那就创建Action,将任务交给dispatcher    if (setPlaceholder) {      setPlaceholder(target, getPlaceholderDrawable());    }    Action action =        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,            errorDrawable, requestKey, tag, callback, noFade);    picasso.enqueueAndSubmit(action);  }
  into方法会先从缓存里面查找图片,如果找不到的话,则会创建Action即一个加载任务,交给Dispatcher执行。

那我们就来看看picasso.enqueueAndSubmit方法做了什么.
在这之前,先来看下Action是什么鬼,
为什么有了Request还要Action.
先看Request有哪些属性:

 int id;  long started;  int networkPolicy;  public final Uri uri;  public final int resourceId;  public final String stableKey;  public final List transformations;  public final int targetWidth;  public final int targetHeight;  public final boolean centerCrop;  public final boolean centerInside;  public final boolean onlyScaleDown;  public final float rotationDegrees;  public final float rotationPivotX;  public final float rotationPivotY;  public final boolean hasRotationPivot;  public final Bitmap.Config config;  public final Priority priority;

再看Action的属性:

 final Picasso picasso;  final Request request;  final WeakReference target;  final boolean noFade;  final int memoryPolicy;  final int networkPolicy;  final int errorResId;  final Drawable errorDrawable;  final String key;  final Object tag;  boolean willReplay;  boolean cancelled;
  Request关注的是请求本身,比如请求的源、id、开始时间、图片变换配置、优先级等等,而Action则代表的是一个加载任务,所以不仅需要

Request对象的引用,还需要Picasso实例,是否重试加载等等

Action有个需要关注的点,那就是WeakReference target,它持有的是Target(比如ImageView..)的弱引用,这样可以保证加载时间很长的情况下
也不会影响到Target的回收了.

好的,那回到刚才的思路,我们开始分析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);  }
 它会先从action任务上拿到对应target,也就是imageView,然后从weakHashMap中通过这个imageView索引到对应的action,如果

发现这个action跟传进来的action不一样的话,那就取消掉之前的加载任务。最后将当前加载任务提交.

**还记得Picasso初始化的时候吗?
他会实例化一个Dispatcher**

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

跟进submit发现最终调用的是Dispatcher的dispatchSubmit(action)方法.这个Dispatcher即任务分发器,它是在
Picasso实例创建的时候初始化的.。

每一个Dispatcher都需要关联线程池(service)、下载器(downloader)、主线程的Handler(HANDLER)、缓存(cache)、
监控器(stats).

这里先看线程池,Picasso默认的线程池叫PicassoExecutorService,它继承自ThreadPoolExecutor,默认线程数量为
3.但是PicassoExecutorService的特性是可以根据网络情况调整线程数量,wifi下是4个线程,而2g网只有一个线程。具体是
通过在Dispatcher中注册了监听网络变化的广播接收者。

另外,PicassoExecutorService中还有一个很重要的方法叫submit,它会去执行一个runnable.

好的,我们回到Dispatcher,这里还需要关注的是Dispatcher中有个内部类叫DispatcherHandler,注意哦,
这个handler是Dispatcher自己的,而不是构造器传进来的。而且,这个handler绑定的是子线程的Looper

**Dispatcher构造函数中会初始化DispactherHandler
而参数中的getLooper 是子线程的loop队列**

 this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);

而dispatcherThread则是一个HandlerThread:
Dispatcher内部类

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

这个线程什么都没做

再回到Picasso. submit(action)方法

subimt方法调用了dispatcher.dispatchSubmit(action);方法

 不用看都知道会发消息给handler。而handler收到这个消息之后调用了这个方法:

dispatcher.performSubmit(action);

//将action封装的请求信息 全部通过handler的方法分发出去//刚刚提到的action内有imageView  url  bitmap一系列信息void dispatchSubmit(Action action) {    handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));  }
 void performSubmit(Action action, boolean dismissFailed) {//注意哦,这里已经不在主线程了,而是在dispatcher线程(HandlerThread)    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;    }

这些requestHandler,看谁可以处理当前请求,如果发现了,那就创建BitmapHandler,并把这个requestHandler传进去,

线程池在收到BitmapHunter之后,会调用其run方法,那么我们就来看下:

@Override public void run() {    try {      updateThreadName(data);      if (picasso.loggingEnabled) {        log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));      }      result = hunt();      if (result == null) {        dispatcher.dispatchFailed(this);      } else {        dispatcher.dispatchComplete(this);      }    } catch (Downloader.ResponseException e) {      if (!e.localCacheOnly || e.responseCode != 504) {        exception = e;      }      dispatcher.dispatchFailed(this);    } catch (NetworkRequestHandler.ContentLengthException e) {      exception = e;      dispatcher.dispatchRetry(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);    }  }

核心逻辑是由hunt方法完成的,下面一堆catch语句分别捕捉不同的异常然后上报给dispatcher进行处理。
而hunt方法里面肯定会调用RequestHandler的load方法:

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;      }    }    //缓存没有命中的话,再调用requestHandler.load    data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;    RequestHandler.Result result = requestHandler.load(data, networkPolicy);    //拿到结果    if (result != null) {      loadedFrom = result.getLoadedFrom();      exifRotation = result.getExifOrientation();       //从结果中拿bitmap      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() || exifRotation != 0) {        synchronized (DECODE_LOCK) {          if (data.needsMatrixTransform() || exifRotation != 0) {            bitmap = transformResult(data, bitmap, exifRotation);            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;  }

这里假设是一个网络请求,那么最终NetworkRequestHandler会处理请求:

@Override public Result load(Request request, int networkPolicy) throws IOException {    //这个downloader也是Dispatcher创建的时候传进来的    Response response = downloader.load(request.uri, request.networkPolicy);    if (response == null) {      return null;    }    //判断是从缓存还是网络拿的    Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;    //从响应中拿到bitmap    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);  }

现在我们关注下这个downloader的前世今生,如果用户没有自定义的话,那将使用默认
downloader:

 downloader = Utils.createDefaultDownloader(context);Utils#createDefaultDownloaderstatic Downloader createDefaultDownloader(Context context) {    try {      Class.forName(com.squareup.okhttp.OkHttpClient);      return OkHttpLoaderCreator.create(context);    } catch (ClassNotFoundException ignored) {    }    return new UrlConnectionDownloader(context);  }
  首先反射下,看有没有依赖okhttp,如果依赖的话,那就使用OkHttpClient喽,否则就使用默认的HttpUrlConnection了。

注:其实从4.4开始,okhttp已经作为HttpUrlConnection的实现引擎了。

可以从picasso的pom文件里面看到,okhttp是optional的:

以UrlConnectionDownloader为例,看下它的load方法:

  @Override public Response load(Uri uri, int networkPolicy) throws IOException {    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {      installCacheIfNeeded(context);    }    HttpURLConnection connection = openConnection(uri);    connection.setUseCaches(true);    if (networkPolicy != 0) {      String headerValue;      if (NetworkPolicy.isOfflineOnly(networkPolicy)) {        headerValue = FORCE_CACHE;      } else {        StringBuilder builder = CACHE_HEADER_BUILDER.get();        builder.setLength(0);        if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {          builder.append(no-cache);        }        if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {          if (builder.length() > 0) {            builder.append(',');          }          builder.append(no-store);        }        headerValue = builder.toString();      }      connection.setRequestProperty(Cache-Control, headerValue);    }    int responseCode = connection.getResponseCode();    if (responseCode >= 300) {      connection.disconnect();      throw new ResponseException(responseCode +   + connection.getResponseMessage(),          networkPolicy, responseCode);    }    long contentLength = connection.getHeaderFieldInt(Content-Length, -1);    boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));    return new Response(connection.getInputStream(), fromCache, contentLength);  }

注意哦,Disk Cache功能是在这里做掉的,它基于Http语义来判断是否缓存.
另,返回的是inputStream流,而不是Bitmap对象.

好的,现在我们回到BitmapHunter#run(),它在拿到结果后会将结果交给dispatcher

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

**我们看dispatcher.dispatchComplete(this),它会把消息发给自己内部的handler,也就是刚才说的Looper在子线程
的handler,handler将做如下处理:**

BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performComplete(hunter);

注意哦,BitmapHunter会持有网络请求回来的Bitmap引用.来看下performComplete:

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);    }  }
  首先会根据事先设置的缓存策略决定是否将结果加到内存缓存。然后调用batch方法,从名字就可以知道,这个方法会把结果暂存,

然后批量处理(等待200ms),这样做也是为了防止短时间大量任务阻塞消息队列。到时间后,就会执行performBatchComplete,
此方法会将这个批次的所有结果一次性发给主线程的Handler,也就是Picasso中定义的Handler:

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

主线程收到消息后会进行处理:

case HUNTER_BATCH_COMPLETE: {          @SuppressWarnings(unchecked) List batch = (List) msg.obj;          //noinspection ForLoopReplaceableByForEach          for (int i = 0, n = batch.size(); i < n; i++) {            BitmapHunter hunter = batch.get(i);            hunter.picasso.complete(hunter);          }
   对batch中每个BitmapHunter调用complete方法,而complete方法会调用deliverAction方法,最终其实调用的是具体

action的complete方法,如果是ImageView的话,那就是ImageViewAction的complete方法:

@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
if (result == null) {
throw new AssertionError(
String.format(Attempted to complete action with no result!
%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();}

}
注意看这一句,ImageView target = this.target.get(),因为target是ImageView的弱引用,在下载过程中,
ImageView可能已经被销毁了,所以这里要做下判断。

如果没有被回收,那么图片最终通过PicassoDrawable.setBitmap()方法被设置到ImageView上.
这个PicassoDrawable提供了fade动画.

简单总结下,当我们执行Picasso.with(context).load(url).into(imageview)时,首先会构造Picasso实例,然后会
根据url创建请求,然后请求会被交给Dispatcher,Dispatcher将在子线程对请求任务进行调度,将请求任务交给线程池
执行,执行完毕后,将结果传给主线程的handler,最后在主线程中将图片设置到ImageView上.

其他需要关注的点
关于缓存策略

 **Picasso的缓存是内存缓存+磁盘缓存,内存缓存基于LruCache类,可配置替换。磁盘缓存依赖于http缓存,不可配置。

先看内存缓存.内存缓存比较简单,是通过LinkedHashMap实现.
读缓存时机:生成了请求Request对象,准备创建Action加载任务之前,会先去缓存里面查找下.**

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

写缓存时机:图片从网络或者其他地方加载成功后,即在BitmapHunter的run方法执行结束的时候.

 if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {      cache.set(hunter.getKey(), hunter.getResult());    }

注意哦,缓存的是经过压缩之后的图片(如果你使用了fit或者resize方法的话),
再看磁盘缓存。
如果你是使用UrlConnectionDownloader的话,那很不幸,缓存只在Api>14上生效,因为缓存依赖于HttpResponseCache.
如果你依赖了okhttp,那么缓存策略始终是有效的。另外需要说明的是,既然是http缓存,那么缓存的可用性依赖于http响应是
否允许缓存,也就是说得看响应中是否携带Cache-Control、Expires等字段.对于这块不了解的话,可以参考我的这篇文章:
HttpCache in android
还有一点,缓存的路径是 应用cache目录/picasso-cache 文件夹.具体代码参考Utils.createDefaultCacheDir方法

关于预加载

首先要注意的是Callback是一个强引用,如果你使用带Callback的重载形式的话,只有当Request结束的时候才会释放
引用,在此期间你的Activity/Fragment等组件引用不会被释放.因此你需要注意内存泄露的情形.

怎么实现?很简单拉,调fetch的时候创建了FetchAction,然后其他流程上面描述的一样,最终在Dispatcher.performComplete
的时候将结果写入内存缓存,结果回传到主线程的时候,调用了FetchAction的complete方法,这里面不对Bitmap做
任何处理就行拉:

@Override void complete(Bitmap result, Picasso.LoadedFrom from) {       if (callback != null) {         callback.onSuccess();       }     }

关于图形变换

图形变换在Picasso中被抽象成Transformation接口,具体的变换操作由transform方法实现.Request维护一个
图形变换的列表List,当图片加载成功后,BitmapHunter中将会遍历这个变换集合,依次进行变换,
最后返回变换后的bitmap.恩,其实是一个回调的思想,将操作封装到接口中交给系统,系统在某个特定时机调用你的接口。

static Bitmap applyCustomTransformations(List transformations, Bitmap result) {             for (int i = 0, count = transformations.size(); i < count; i++) {               final Transformation transformation = transformations.get(i);               Bitmap newResult;               try {                 newResult = transformation.transform(result);               } catch (final RuntimeException e) {                 Picasso.HANDLER.post(new Runnable() {                   @Override public void run() {                     throw new RuntimeException(                         Transformation  + transformation.key() +  crashed with exception., e);                   }                 });                 return null;               }               ....               result = newResult;             }             return result;           } 

关于CleanupThread

Picasso类中有一个内部线程叫CleanupThread,这是一个daemon线程,它的工作是找到那些Target(比如说ImageView)已经被回收
但是所对应的Request请求还在继续的任务(Action),找到之后,会取消对应的请求,避免资源浪费.

看下代码:

private static class CleanupThread extends Thread {    private final ReferenceQueue referenceQueue;    private final Handler handler;    CleanupThread(ReferenceQueue referenceQueue, Handler handler) {//关联主线程的handler,refreenceQueue      this.referenceQueue = referenceQueue;      this.handler = handler;      setDaemon(true);      setName(THREAD_PREFIX + refQueue);    }    @Override public void run() {      Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);      while (true) {        try {          // Prior to Android 5.0, even when there is no local variable, the result from          // remove() & obtainMessage() is kept as a stack local variable.          // We're forcing this reference to be cleared and replaced by looping every second          // when there is nothing to do.          // This behavior has been tested and reproduced with heap dumps.          RequestWeakReference remove =              (RequestWeakReference) referenceQueue.remove(THREAD_LEAK_CLEANING_MS);          Message message = handler.obtainMessage();          if (remove != null) {            message.what = REQUEST_GCED;            message.obj = remove.action;            handler.sendMessage(message);          } else {            message.recycle();          }        } catch (InterruptedException e) {          break;        } catch (final Exception e) {          handler.post(new Runnable() {            @Override public void run() {              throw new RuntimeException(e);            }          });          break;        }      }    }    void shutdown() {      interrupt();    }  }

可以看到它会不断轮询ReferenceQueue,找到这样的reference,就交给handler,handler会从reference中拿到action,
并取消请求.

 case REQUEST_GCED: {          Action action = (Action) msg.obj;          if (action.getPicasso().loggingEnabled) {            log(OWNER_MAIN, VERB_CANCELED, action.request.logId(), target got garbage collected);          }          action.picasso.cancelExistingRequest(action.getTarget());          break;        }

那么这个ReferenceQueue又是如何关联Action的呢?这个可以从Action的构造器中拿到答案:

可以看到两点:

每个Action都会关联Picasso中唯一的referenceQueue实例; 每个RequestWeakReference都会同时关联Target和Action.
resume/pause

pause

流程如下。
这里写图片描述

可能会有疑问的地方在于Dispatcher#performPauseTag中遍历所有的hunter,都会调一次cancel,这似乎会取消所有
的请求。但其实不是这样的,可以看下BitmapHunter#cancel方法的代码:

  boolean cancel() {     return action == null         && (actions == null || actions.isEmpty())         && future != null         && future.cancel(false);   }

注意到它会判断action是否为空,如果不为空就不会取消了。而在Dispatcher#performPauseTag中会把tag匹配的
action与对应的BitmapHunter解绑(detach),让BitmapHunter的action为空.所以这并不影响其他任务的执行。

resume

流程如下。

这里写图片描述

考BitmapHunter#decodeStream、RequestHandler#createBitmapOptions、RequestHandler#calculateInSampleSize
这三个方法,有个需要注意的地方,只有当设置图片的宽高时(调用了fit或者resize)才会计算smpleSize进行压缩。

0 0
原创粉丝点击