• 图片加载框架Picasso解析


    picasso是Square公司开源的一个Android图形缓存库

    主要有以下一些特性:

    在adapter中回收和取消当前的下载;

    使用最少的内存完成复杂的图形转换操作;

    自动的内存和硬盘缓存;

    图形转换操作,如变换大小,旋转等,提供了接口来让用户可以自定义转换操作;

    加载载网络或本地资源;

    Picasso.class

    他有一个内部类,一般是通过他来创建实例的:

    downloader(Downloader downloader) :

    容许使用自定义的下载器,可以用okhttp或者volley,必须实现Downloader接口。

    executor(ExecutorService executorService):

    容许使用自己的线程池来进行下载

    memoryCache(Cache memoryCache):

    容许使用自己的缓存类,必须实现Cache接口。

    requestTransformer(RequestTransformer transformer):

    listener(Listener listener):

    addRequestHandler(RequestHandler requestHandler):

    indicatorsEnabled(boolean enabled):设置图片来源的指示器。

    loggingEnabled(boolean enabled):

    再来看build()方法:

    [html] view plain copy
     
    1. /** Create the {@link Picasso} instance. */  
    2.    public Picasso build() {  
    3.      Context context = this.context;  
    4.   
    5.      if (downloader == null) {  
    6.        downloader = Utils.createDefaultDownloader(context);  
    7.      }  
    8.      if (cache == null) {  
    9.        cache = new LruCache(context);  
    10.      }  
    11.      if (service == null) {  
    12.        service = new PicassoExecutorService();  
    13.      }  
    14.      if (transformer == null) {  
    15.        transformer = RequestTransformer.IDENTITY;  
    16.      }  
    17.   
    18.      Stats stats = new Stats(cache);  
    19.   
    20.      Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);  
    21.   
    22.      return new Picasso(context, dispatcher, cache, listener, transformer,  
    23.          requestHandlers, stats, indicatorsEnabled, loggingEnabled);  
    24.    }  

    这里会使用默认的下载器,缓存类,线程池,并将这些对象封装到了分发器Dispatcher里。然后在返回一个Picasso对象。

    一般情况下,如果不需要自定义bulid里的这些方法,可以使用Picasso.with(context)默认方法来获得单例对象:

    [html] view plain copy
     
    1. public static Picasso with(Context context) {  
    2.     if (singleton == null) {  
    3.       synchronized (Picasso.class) {  
    4.         if (singleton == null) {  
    5.           singleton = new Builder(context).build();  
    6.         }  
    7.       }  
    8.     }  
    9.     return singleton;  
    10.   }  

    如果需要自定义一些对象:

    [html] view plain copy
     
    1. public class SamplePicassoFactory {  
    2.   
    3.   private static Picasso sPicasso;  
    4.   
    5.   public static Picasso getPicasso(Context context) {  
    6.     if (sPicasso == null) {  
    7.         sPicasso = new Picasso.Builder(context)  
    8.             .downloader(new OkHttpDownloader(context, ConfigConstants.MAX_DISK_CACHE_SIZE))  
    9.             .memoryCache(new LruCache(ConfigConstants.MAX_MEMORY_CACHE_SIZE))  
    10.             .build();  
    11.     }  
    12.     return sPicasso;  
    13.   }  
    14. }  

    通过上面的方法,获得sPicasso对象后就设置成了单例,但是最好设置成双重校验锁模式。

    如果通过以上方法获得对象后,还可以通过Picasso.setSingletonInstance(Picasso picasso)方法设置对象到Picasso中,这样以后还是通过Picasso.with(context)来调用。

    接着就可以通过以下方式设置加载源:

    可以是uri地址,file文件,res资源drawable。

    最终都是通过以下方法来创建一个RequestCreator对象:

    [html] view plain copy
     
    1. public RequestCreator load(Uri uri) {  
    2.     return new RequestCreator(this, uri, 0);  
    3.   }  
    [html] view plain copy
     
    1. RequestCreator(Picasso picasso, Uri uri, int resourceId) {  
    2.     if (picasso.shutdown) {  
    3.       throw new IllegalStateException(  
    4.           "Picasso instance already shut down. Cannot submit new requests.");  
    5.     }  
    6.     this.picasso = picasso;  
    7.     this.data = new Request.Builder(uri, resourceId);   
    8.   }  

    RequestCreator对象是用来设置一系列属性的,如:


    noPlaceholder():设置没有加载等待图片

    placeholder(int placeholderResId):设置加载等待图片

    placeholder(Drawable placeholderDrawable):设置加载等待图片

    error(int errorResId):设置加载出错的图片。

    error(Drawable errorDrawable):设置加载出错的图片。

    tag(Object tag):设置标记

    fit():自适应,下载的图片有多少像素就显示多少像素

    resizeDimen(int targetWidthResId, int targetHeightResId):设置图片显示的像素。

    resize(int targetWidth, int targetHeight):设置图片显示的像素。

    centerCrop():设置ImageView的ScaleType属性.

    centerInside():设置ImageView的ScaleType属性.

    rotate(float degrees):设置旋转角度。

    rotate(float degrees, float pivotX, float pivotY):设置以某个中心点设置某个旋转角度。

    config(Bitmap.Config config):设置Bitmap的Config属性

    priority(Priority priority):设置请求的优先级。

    transform(Transformation transformation):

    skipMemoryCache():跳过内存缓存

    memoryPolicy(MemoryPolicy policy, MemoryPolicy... additional):

    networkPolicy(NetworkPolicy policy, NetworkPolicy... additional):

    noFade():没有淡入淡出效果

    get():获得bitmap对象

    fetch():

    设置完以上一系列属性之后,最关键的就是into方法,现在以into(ImageView target)举例:

    [html] view plain copy
     
    1. public void into(ImageView target) {  
    2.     into(target, null);  
    3.   }  

    他实际调用的是:

    [html] view plain copy
     
    1. public void into(ImageView target, Callback callback) {  
    2.    long started = System.nanoTime();  
    3.    checkMain();  
    4.   
    5.    if (target == null) {  
    6.      throw new IllegalArgumentException("Target must not be null.");  
    7.    }  
    8.   
    9.    if (!data.hasImage()) {  
    10.      picasso.cancelRequest(target);  
    11.      if (setPlaceholder) {  
    12.        setPlaceholder(target, getPlaceholderDrawable());  
    13.      }  
    14.      return;  
    15.    }  
    16.   
    17.    if (deferred) {  
    18.      if (data.hasSize()) {  
    19.        throw new IllegalStateException("Fit cannot be used with resize.");  
    20.      }  
    21.      int width = target.getWidth();  
    22.      int height = target.getHeight();  
    23.      if (width == 0 || height == 0) {  
    24.        if (setPlaceholder) {  
    25.          setPlaceholder(target, getPlaceholderDrawable());  
    26.        }  
    27.        picasso.defer(target, new DeferredRequestCreator(this, target, callback));  
    28.        return;  
    29.      }  
    30.      data.resize(width, height);  
    31.    }  
    32.   
    33.    Request request = createRequest(started);  
    34.    String requestKey = createKey(request);  
    35.   
    36.    if (shouldReadFromMemoryCache(memoryPolicy)) {  
    37.      Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);  
    38.      if (bitmap != null) {  
    39.        picasso.cancelRequest(target);  
    40.        setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);  
    41.        if (picasso.loggingEnabled) {  
    42.          log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);  
    43.        }  
    44.        if (callback != null) {  
    45.          callback.onSuccess();  
    46.        }  
    47.        return;  
    48.      }  
    49.    }  
    50.   
    51.    if (setPlaceholder) {  
    52.      setPlaceholder(target, getPlaceholderDrawable());  
    53.    }  
    54.   
    55.    Action action =  
    56.        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,  
    57.            errorDrawable, requestKey, tag, callback, noFade);  
    58.   
    59.    picasso.enqueueAndSubmit(action);  
    60.  }  
    [html] view plain copy
     
    1. checkMain();  

    首先检查是否是主线程

    [html] view plain copy
     
    1. if (target == null) {  
    2.       throw new IllegalArgumentException("Target must not be null.");  
    3.     }  

    检查目标view是否存在

    [html] view plain copy
     
    1. if (!data.hasImage()) {  
    2.       picasso.cancelRequest(target);  
    3.       if (setPlaceholder) {  
    4.         setPlaceholder(target, getPlaceholderDrawable());  
    5.       }  
    6.       return;  
    7.     }  

    如果没有设置uri或者resDrawable,就停止请求,如果设置了加载等待的图片就设置,然后就return

    [html] view plain copy
     
    1. if (deferred) {  
    2.       if (data.hasSize()) {  
    3.         throw new IllegalStateException("Fit cannot be used with resize.");  
    4.       }  
    5.       int width = target.getWidth();  
    6.       int height = target.getHeight();  
    7.       if (width == 0 || height == 0) {  
    8.         if (setPlaceholder) {  
    9.           setPlaceholder(target, getPlaceholderDrawable());  
    10.         }  
    11.         picasso.defer(target, new DeferredRequestCreator(this, target, callback));  
    12.         return;  
    13.       }  
    14.       data.resize(width, height);  
    15.     }  

    如果设置fit自适应:如果已经设置了图片像素大小就抛异常,如果目标view的长宽等于0,就在设置等待图片后延期处理,如果不等于0就设置size到Request.Builder的data里。

    [html] view plain copy
     
    1. Request request = createRequest(started);  
    2.  String requestKey = createKey(request);  

    接着就创建Request,并生成一个String类型的requestKey。

    [html] view plain copy
     
    1. /** Create the request optionally passing it through the request transformer. */  
    2.   private Request createRequest(long started) {  
    3.     int id = nextId.getAndIncrement();  
    4.   
    5.     Request request = data.build();  
    6.     request.id = id;  
    7.     request.started = started;  
    8.   
    9.     boolean loggingEnabled = picasso.loggingEnabled;  
    10.     if (loggingEnabled) {  
    11.       log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());  
    12.     }  
    13.   
    14.     Request transformed = picasso.transformRequest(request);  
    15.     if (transformed != request) {  
    16.       // If the request was changed, copy over the id and timestamp from the original.  
    17.       transformed.id = id;  
    18.       transformed.started = started;  
    19.   
    20.       if (loggingEnabled) {  
    21.         log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);  
    22.       }  
    23.     }  
    24.   
    25.     return transformed;  
    26.   }  

    在以上代码可以看出,这里调用picasso.transformRequest(request);来给使用者提供一个可以更改request的机会,他最终调用的是Picasso.Build里面的通过requestTransformer(RequestTransformer transformer)方法传进去的RequestTransformer 接口:

    [html] view plain copy
     
    1. public interface RequestTransformer {  
    2.    /**  
    3.     * Transform a request before it is submitted to be processed.  
    4.     *  
    5.     * @return The original request or a new request to replace it. Must not be null.  
    6.     */  
    7.    Request transformRequest(Request request);  
    8.   
    9.    /** A {@link RequestTransformer} which returns the original request. */  
    10.    RequestTransformer IDENTITY = new RequestTransformer() {  
    11.      @Override public Request transformRequest(Request request) {  
    12.        return request;  
    13.      }  
    14.    };  
    15.  }  

    默认使用的是IDENTITY。这里没有做任何的修改,如果有需要可以自己设置接口以达到修改Request的目的。

    [html] view plain copy
     
    1. if (shouldReadFromMemoryCache(memoryPolicy)) {  
    2.       Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);  
    3.       if (bitmap != null) {  
    4.         picasso.cancelRequest(target);  
    5.         setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);  
    6.         if (picasso.loggingEnabled) {  
    7.           log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);  
    8.         }  
    9.         if (callback != null) {  
    10.           callback.onSuccess();  
    11.         }  
    12.         return;  
    13.       }  
    14.     }  

    接着要判断是否需要从缓存中读取图片,如果需要,就要根据requestKey从缓存中读取:

    [html] view plain copy
     
    1. Bitmap quickMemoryCacheCheck(String key) {  
    2.     Bitmap cached = cache.get(key);  
    3.     if (cached != null) {  
    4.       stats.dispatchCacheHit();  
    5.     } else {  
    6.       stats.dispatchCacheMiss();  
    7.     }  
    8.     return cached;  
    9.   }  

    这里的cache用的是LruCache内存缓存类,这个内存缓存类,实现了Cache接口,最后调用的是:

    [html] view plain copy
     
    1. @Override public Bitmap get(String key) {  
    2.    if (key == null) {  
    3.      throw new NullPointerException("key == null");  
    4.    }  
    5.   
    6.    Bitmap mapValue;  
    7.    synchronized (this) {  
    8.      mapValue = map.get(key);  
    9.      if (mapValue != null) {  
    10.        hitCount++;  
    11.        return mapValue;  
    12.      }  
    13.      missCount++;  
    14.    }  
    15.   
    16.    return null;  
    17.  }  

    这里的map是LinkedHashMap<String, Bitmap>:

    [html] view plain copy
     
    1. this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);  

    缓存的策略稍后分析,现在先回到前面,如果从缓存中拿到的bitmap不等于null,就调用picasso.cancelRequest(target)来删除请求,然后通过

    [html] view plain copy
     
    1. setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);  

    设置图片,然后如果设置了callback回调,再回掉callback.onSuccess();方法,然后就return。

    如果没有设置内存缓存,或者缓存中的图片已经不存在:

    [html] view plain copy
     
    1. if (setPlaceholder) {  
    2.      setPlaceholder(target, getPlaceholderDrawable());  
    3.    }  
    4.   
    5.    Action action =  
    6.        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,  
    7.            errorDrawable, requestKey, tag, callback, noFade);  
    8.   
    9.    picasso.enqueueAndSubmit(action);  

    接着就设置等待加载图片,然后封装一个action,然后将action加入到分发器中:

    [html] view plain copy
     
    1. void enqueueAndSubmit(Action action) {  
    2.     Object target = action.getTarget();  
    3.     if (target != null && targetToAction.get(target) != action) {  
    4.       // This will also check we are on the main thread.  
    5.       cancelExistingRequest(target);  
    6.       targetToAction.put(target, action);  
    7.     }  
    8.     submit(action);  
    9.   }  
    10.   
    11.   void submit(Action action) {  
    12.     dispatcher.dispatchSubmit(action);  
    13.   }  

    然后调用了dispatcher的performSubmit()方法:

    [html] view plain copy
     
    1. void performSubmit(Action action) {  
    2.    performSubmit(action, true);  
    3.  }  
    4.   
    5.  void performSubmit(Action action, boolean dismissFailed) {  
    6.    if (pausedTags.contains(action.getTag())) {  
    7.      pausedActions.put(action.getTarget(), action);  
    8.      if (action.getPicasso().loggingEnabled) {  
    9.        log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),  
    10.            "because tag '" + action.getTag() + "' is paused");  
    11.      }  
    12.      return;  
    13.    }  
    14.   
    15.    BitmapHunter hunter = hunterMap.get(action.getKey());  
    16.    if (hunter != null) {  
    17.      hunter.attach(action);  
    18.      return;  
    19.    }  
    20.   
    21.    if (service.isShutdown()) {  
    22.      if (action.getPicasso().loggingEnabled) {  
    23.        log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");  
    24.      }  
    25.      return;  
    26.    }  
    27.   
    28.    hunter = forRequest(action.getPicasso(), this, cache, stats, action);  
    29.    hunter.future = service.submit(hunter);  
    30.    hunterMap.put(action.getKey(), hunter);  
    31.    if (dismissFailed) {  
    32.      failedActions.remove(action.getTarget());  
    33.    }  
    34.   
    35.    if (action.getPicasso().loggingEnabled) {  
    36.      log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());  
    37.    }  
    38.  }  

    这里通过 hunter = forRequest(action.getPicasso(), this, cache, stats, action);获得一个BitmapHunter对象,接着就提交到线程池中 hunter.future = service.submit(hunter);:

    接着线程池就会调用hunter 的run方法,因为这实现了Runnable对象:

    [html] view plain copy
     
    1. @Override public void run() {  
    2.    try {  
    3.      updateThreadName(data);  
    4.   
    5.      if (picasso.loggingEnabled) {  
    6.        log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));  
    7.      }  
    8.   
    9.      result = hunt();  
    10.   
    11.      if (result == null) {  
    12.        dispatcher.dispatchFailed(this);  
    13.      } else {  
    14.        dispatcher.dispatchComplete(this);  
    15.      }  
    16.    } catch (Downloader.ResponseException e) {  
    17.      if (!e.localCacheOnly || e.responseCode != 504) {  
    18.        exception = e;  
    19.      }  
    20.      dispatcher.dispatchFailed(this);  
    21.    } catch (NetworkRequestHandler.ContentLengthException e) {  
    22.      exception = e;  
    23.      dispatcher.dispatchRetry(this);  
    24.    } catch (IOException e) {  
    25.      exception = e;  
    26.      dispatcher.dispatchRetry(this);  
    27.    } catch (OutOfMemoryError e) {  
    28.      StringWriter writer = new StringWriter();  
    29.      stats.createSnapshot().dump(new PrintWriter(writer));  
    30.      exception = new RuntimeException(writer.toString(), e);  
    31.      dispatcher.dispatchFailed(this);  
    32.    } catch (Exception e) {  
    33.      exception = e;  
    34.      dispatcher.dispatchFailed(this);  
    35.    } finally {  
    36.      Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);  
    37.    }  
    38.  }  

    接着调用hunt()方法:

    [html] view plain copy
     
    1. Bitmap hunt() throws IOException {  
    2.     Bitmap bitmap = null;  
    3.   
    4.     if (shouldReadFromMemoryCache(memoryPolicy)) {  
    5.       bitmap = cache.get(key);  
    6.       if (bitmap != null) {  
    7.         stats.dispatchCacheHit();  
    8.         loadedFrom = MEMORY;  
    9.         if (picasso.loggingEnabled) {  
    10.           log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");  
    11.         }  
    12.         return bitmap;  
    13.       }  
    14.     }  
    15.   
    16.     data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;  
    17.     RequestHandler.Result result = requestHandler.load(data, networkPolicy);  
    18.     if (result != null) {  
    19.       loadedFrom = result.getLoadedFrom();  
    20.       exifRotation = result.getExifOrientation();  
    21.   
    22.       bitmap = result.getBitmap();  
    23.   
    24.       // If there was no Bitmap then we need to decode it from the stream.  
    25.       if (bitmap == null) {  
    26.         InputStream is = result.getStream();  
    27.         try {  
    28.           bitmap = decodeStream(is, data);  
    29.         } finally {  
    30.           Utils.closeQuietly(is);  
    31.         }  
    32.       }  
    33.     }  
    34.   
    35.     if (bitmap != null) {  
    36.       if (picasso.loggingEnabled) {  
    37.         log(OWNER_HUNTER, VERB_DECODED, data.logId());  
    38.       }  
    39.       stats.dispatchBitmapDecoded(bitmap);  
    40.       if (data.needsTransformation() || exifRotation != 0) {  
    41.         synchronized (DECODE_LOCK) {  
    42.           if (data.needsMatrixTransform() || exifRotation != 0) {  
    43.             bitmap = transformResult(data, bitmap, exifRotation);  
    44.             if (picasso.loggingEnabled) {  
    45.               log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());  
    46.             }  
    47.           }  
    48.           if (data.hasCustomTransformations()) {  
    49.             bitmap = applyCustomTransformations(data.transformations, bitmap);  
    50.             if (picasso.loggingEnabled) {  
    51.               log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");  
    52.             }  
    53.           }  
    54.         }  
    55.         if (bitmap != null) {  
    56.           stats.dispatchBitmapTransformed(bitmap);  
    57.         }  
    58.       }  
    59.     }  
    60.   
    61.     return bitmap;  
    62.   }  

    这里如果从内存缓存中渠道bitmap对象,就直接返回了;否则通过requestHandler.load(data, networkPolicy);来发起网络请求(这个是NetworkRequestHandler类型的):

    [html] view plain copy
     
    1. @Override public Result load(Request request, int networkPolicy) throws IOException {  
    2.     Response response = downloader.load(request.uri, request.networkPolicy);  
    3.     if (response == null) {  
    4.       return null;  
    5.     }  
    6.   
    7.     Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;  
    8.   
    9.     Bitmap bitmap = response.getBitmap();  
    10.     if (bitmap != null) {  
    11.       return new Result(bitmap, loadedFrom);  
    12.     }  
    13.   
    14.     InputStream is = response.getInputStream();  
    15.     if (is == null) {  
    16.       return null;  
    17.     }  
    18.     // Sometimes response content length is zero when requests are being replayed. Haven't found  
    19.     // root cause to this but retrying the request seems safe to do so.  
    20.     if (loadedFrom == DISK && response.getContentLength() == 0) {  
    21.       Utils.closeQuietly(is);  
    22.       throw new ContentLengthException("Received response with 0 content-length header.");  
    23.     }  
    24.     if (loadedFrom == NETWORK && response.getContentLength() > 0) {  
    25.       stats.dispatchDownloadFinished(response.getContentLength());  
    26.     }  
    27.     return new Result(is, loadedFrom);  
    28.   }  

    这里调用了downloader.load(request.uri, request.networkPolicy)方法,这是一个UrlConnectionDownloader类型的对象,调用的是UrlConnectionDownloader的load()方法:

    [html] view plain copy
     
    1. @Override public Response load(Uri uri, int networkPolicy) throws IOException {  
    2.    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {  
    3.      installCacheIfNeeded(context);  
    4.    }  
    5.   
    6.    HttpURLConnection connection = openConnection(uri);  
    7.    connection.setUseCaches(true);  
    8.   
    9.    if (networkPolicy != 0) {  
    10.      String headerValue;  
    11.   
    12.      if (NetworkPolicy.isOfflineOnly(networkPolicy)) {  
    13.        headerValue = FORCE_CACHE;  
    14.      } else {  
    15.        StringBuilder builder = CACHE_HEADER_BUILDER.get();  
    16.        builder.setLength(0);  
    17.   
    18.        if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {  
    19.          builder.append("no-cache");  
    20.        }  
    21.        if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {  
    22.          if (builder.length() > 0) {  
    23.            builder.append(',');  
    24.          }  
    25.          builder.append("no-store");  
    26.        }  
    27.   
    28.        headerValue = builder.toString();  
    29.      }  
    30.   
    31.      connection.setRequestProperty("Cache-Control", headerValue);  
    32.    }  
    33.   
    34.    int responseCode = connection.getResponseCode();  
    35.    if (responseCode >= 300) {  
    36.      connection.disconnect();  
    37.      throw new ResponseException(responseCode + " " + connection.getResponseMessage(),  
    38.          networkPolicy, responseCode);  
    39.    }  
    40.   
    41.    long contentLength = connection.getHeaderFieldInt("Content-Length", -1);  
    42.    boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));  
    43.   
    44.    return new Response(connection.getInputStream(), fromCache, contentLength);  
    45.  }  

    如果系统版本是4.0以上,就使用installCacheIfNeeded(context)来启动datadata包名cacha下的某个目录的磁盘缓存:

    [html] view plain copy
     
    1. private static void installCacheIfNeeded(Context context) {  
    2.    // DCL + volatile should be safe after Java 5.  
    3.    if (cache == null) {  
    4.      try {  
    5.        synchronized (lock) {  
    6.          if (cache == null) {  
    7.            cache = ResponseCacheIcs.install(context);  
    8.          }  
    9.        }  
    10.      } catch (IOException ignored) {  
    11.      }  
    12.    }  
    13.  }  
    14.   
    15.  private static class ResponseCacheIcs {  
    16.    static Object install(Context context) throws IOException {  
    17.      File cacheDir = Utils.createDefaultCacheDir(context);  
    18.      HttpResponseCache cache = HttpResponseCache.getInstalled();  
    19.      if (cache == null) {  
    20.        long maxSize = Utils.calculateDiskCacheSize(cacheDir);  
    21.        cache = HttpResponseCache.install(cacheDir, maxSize);  
    22.      }  
    23.      return cache;  
    24.    }  

    但是如果要是用这个磁盘缓存,就要在HttpURLConnection的响应头上加上缓存的控制头"Cache-Control"!

    最后返回new Response(connection.getInputStream(), fromCache, contentLength)请求结果。

    接着回到上面的hunt()方法的流程继续:

    [html] view plain copy
     
    1. if (result != null) {  
    2.      loadedFrom = result.getLoadedFrom();  
    3.      exifRotation = result.getExifOrientation();  
    4.   
    5.      bitmap = result.getBitmap();  
    6.   
    7.      // If there was no Bitmap then we need to decode it from the stream.  
    8.      if (bitmap == null) {  
    9.        InputStream is = result.getStream();  
    10.        try {  
    11.          bitmap = decodeStream(is, data);  
    12.        } finally {  
    13.          Utils.closeQuietly(is);  
    14.        }  
    15.      }  
    16.    }  
    17.   
    18.    if (bitmap != null) {  
    19.      if (picasso.loggingEnabled) {  
    20.        log(OWNER_HUNTER, VERB_DECODED, data.logId());  
    21.      }  
    22.      stats.dispatchBitmapDecoded(bitmap);  
    23.      if (data.needsTransformation() || exifRotation != 0) {  
    24.        synchronized (DECODE_LOCK) {  
    25.          if (data.needsMatrixTransform() || exifRotation != 0) {  
    26.            bitmap = transformResult(data, bitmap, exifRotation);  
    27.            if (picasso.loggingEnabled) {  
    28.              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());  
    29.            }  
    30.          }  
    31.          if (data.hasCustomTransformations()) {  
    32.            bitmap = applyCustomTransformations(data.transformations, bitmap);  
    33.            if (picasso.loggingEnabled) {  
    34.              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");  
    35.            }  
    36.          }  
    37.        }  
    38.        if (bitmap != null) {  
    39.          stats.dispatchBitmapTransformed(bitmap);  
    40.        }  
    41.      }  
    42.    }  
    43.   
    44.    return bitmap;  

    然后bitmap返回到run()方法里面的result应用上:

    [html] view plain copy
     
    1. if (result == null) {  
    2.        dispatcher.dispatchFailed(this);  
    3.      } else {  
    4.        dispatcher.dispatchComplete(this);  
    5.      }  

    如果result有结果就分发完成消息,最后将会调用到Picasso里面:

    [html] view plain copy
     
    1. static final Handler HANDLER = new Handler(Looper.getMainLooper()) {  
    2.    @Override public void handleMessage(Message msg) {  
    3.      switch (msg.what) {  
    4.        case HUNTER_BATCH_COMPLETE: {  
    5.          @SuppressWarnings("unchecked") List<BitmapHunterbatch = (List<BitmapHunter>) msg.obj;  
    6.          //noinspection ForLoopReplaceableByForEach  
    7.          for (int i = 0, n = batch.size(); i n; i++) {  
    8.            BitmapHunter hunter = batch.get(i);  
    9.            hunter.picasso.complete(hunter);  
    10.          }  
    11.          break;  
    12.        }  
    [html] view plain copy
     
    1. void complete(BitmapHunter hunter) {  
    2.    Action single = hunter.getAction();  
    3.    List<Actionjoined = hunter.getActions();  
    4.   
    5.    boolean hasMultiple = joined != null && !joined.isEmpty();  
    6.    boolean shouldDeliver = single != null || hasMultiple;  
    7.   
    8.    if (!shouldDeliver) {  
    9.      return;  
    10.    }  
    11.   
    12.    Uri uri = hunter.getData().uri;  
    13.    Exception exception = hunter.getException();  
    14.    Bitmap result = hunter.getResult();  
    15.    LoadedFrom from = hunter.getLoadedFrom();  
    16.   
    17.    if (single != null) {  
    18.      deliverAction(result, from, single);  
    19.    }  
    20.   
    21.    if (hasMultiple) {  
    22.      //noinspection ForLoopReplaceableByForEach  
    23.      for (int i = 0, n = joined.size(); i n; i++) {  
    24.        Action join = joined.get(i);  
    25.        deliverAction(result, from, join);  
    26.      }  
    27.    }  
    28.   
    29.    if (listener != null && exception != null) {  
    30.      listener.onImageLoadFailed(this, uri, exception);  
    31.    }  
    32.  }  

    接着分发action:

    [html] view plain copy
     
    1. private void deliverAction(Bitmap result, LoadedFrom from, Action action) {  
    2.     if (action.isCancelled()) {  
    3.       return;  
    4.     }  
    5.     if (!action.willReplay()) {  
    6.       targetToAction.remove(action.getTarget());  
    7.     }  
    8.     if (result != null) {  
    9.       if (from == null) {  
    10.         throw new AssertionError("LoadedFrom cannot be null.");  
    11.       }  
    12.       action.complete(result, from);  
    13.       if (loggingEnabled) {  
    14.         log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);  
    15.       }  
    16.     } else {  
    17.       action.error();  
    18.       if (loggingEnabled) {  
    19.         log(OWNER_MAIN, VERB_ERRORED, action.request.logId());  
    20.       }  
    21.     }  
    22.   }  

    然后调用的是ImageViewAction里面的action.complete(result, from)方法:

    [html] view plain copy
     
    1. @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {  
    2.    if (result == null) {  
    3.      throw new AssertionError(  
    4.          String.format("Attempted to complete action with no result! %s", this));  
    5.    }  
    6.   
    7.    ImageView target = this.target.get();  
    8.    if (target == null) {  
    9.      return;  
    10.    }  
    11.   
    12.    Context context = picasso.context;  
    13.    boolean indicatorsEnabled = picasso.indicatorsEnabled;  
    14.    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);  
    15.   
    16.    if (callback != null) {  
    17.      callback.onSuccess();  
    18.    }  
    19.  }  

    最后一步通过PicassoDrawable.setBitmap来设置,就算大功完成了。

    附上部分流程图:






  • 相关阅读:
    【Git】git使用
    【Git】git使用
    【Git】git使用
    【spring boot】SpringBoot初学(8)– 简单整合redis
    「Flink」使用Managed Keyed State实现计数窗口功能
    「Flink」Flink的状态管理与容错
    「数据挖掘入门序列」数据挖掘模型分类与预测
    「Flink」使用Java lambda表达式实现Flink WordCount
    Java 8 函数式编程
    「数据挖掘入门系列」数据挖掘模型之分类和预测
  • 原文地址:https://www.cnblogs.com/AceIsSunshineRain/p/5196988.html
Copyright © 2020-2023  润新知