Picasso分析02

来源:互联网 发布:杨子天珠淘宝店 编辑:程序博客网 时间:2024/05/18 15:29

1 Downloader

1 Downloader接口

负责下载图片

//A mechanism机制 to load images from external resources such as a disk cache and/or the internet. public interface Downloader {  // Downloader.Response containing either a Bitmap representation of the request or an// InputStream for the image data. null can be returned to indicate a problem loading the bitmap.  // 之前分析的NetworkRequestHandler中使用到了这个函数---函数1  Response load(Uri uri, int networkPolicy) throws IOException;  // Allows to perform a clean up for this {@link DDDownloader} including closing the disk cache  // and other resources.   void shutdown(); // ---函数2  /** Thrown for non-2XX responses. 响应码不是2开头*/  class ResponseException extends IOException {    final boolean localCacheOnly;    final int responseCode;    public ResponseException(String message, int networkPolicy, int responseCode) {      super(message);      this.localCacheOnly = NetworkPolicy.isOfflineOnly(networkPolicy);      this.responseCode = responseCode;    }  }  /** Response stream or bitmap and info. */  class Response {    final InputStream stream;    final Bitmap bitmap;    final boolean cached;    final long contentLength;    // Response stream and info.    public Response(InputStream stream, boolean loadedFromCache, long contentLength) {      if (stream == null) {        throw new IllegalArgumentException("Stream may not be null.");      }      this.stream = stream;      this.bitmap = null;      this.cached = loadedFromCache;      this.contentLength = contentLength;    }  }}

2 OkHttpDownloader

如果有Okhttp的包,就用这个downloader进行下载
先来看下NetworkPolicy 和 MemoryPolicy

public enum NetworkPolicy {//网络缓冲管理  /** Skips checking the disk cache and forces loading through the network. */  NO_CACHE(1 << 0), // 调用下面的构造函数  /**   * Skips storing the result into the disk cache.   * <em>Note</em>: At this time this is only supported if you are using OkHttp.   */  NO_STORE(1 << 1),  /** Forces the request through the disk cache only, skipping network. */  OFFLINE(1 << 2);  public static boolean shouldReadFromDiskCache(int networkPolicy) {    return (networkPolicy & NetworkPolicy.NO_CACHE.index) == 0;//注意使用了类下的index变量  }  public static boolean shouldWriteToDiskCache(int networkPolicy) {    return (networkPolicy & NetworkPolicy.NO_STORE.index) == 0;  }  public static boolean isOfflineOnly(int networkPolicy) {    return (networkPolicy & NetworkPolicy.OFFLINE.index) != 0;  }  final int index;  private NetworkPolicy(int index) {    this.index = index;  }}public enum MemoryPolicy { // 内存缓冲管理  /** Skips memory cache lookup when processing a request. */  NO_CACHE(1 << 0),  /**   * Skips storing the final result into memory cache. Useful for one-off requests   * to avoid evicting other bitmaps from the cache.   */  NO_STORE(1 << 1);  static boolean shouldReadFromMemoryCache(int memoryPolicy) {    return (memoryPolicy & MemoryPolicy.NO_CACHE.index) == 0;  }  static boolean shouldWriteToMemoryCache(int memoryPolicy) {    return (memoryPolicy & MemoryPolicy.NO_STORE.index) == 0;  }  final int index;  private MemoryPolicy(int index) {    this.index = index;  }}
/** A Downloader which uses OkHttp to download images. */public class OkHttpDownloader implements Downloader {  private static OkHttpClient defaultOkHttpClient() {    OkHttpClient client = new OkHttpClient();    client.setConnectTimeout(Utils.DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);//15000    client.setReadTimeout(Utils.DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);//20000    client.setWriteTimeout(Utils.DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);//20000    return client;  }  private final OkHttpClient client;    /**   * Create new downloader that uses OkHttp. This will install an image cache into the specified   * directory.   * @param cacheDir The directory in which the cache should be stored   * @param maxSize The size limit for the cache.   */  public OkHttpDownloader(final File cacheDir, final long maxSize) {    this(defaultOkHttpClient()); // ---    try {      client.setCache(new com.squareup.okhttp.Cache(cacheDir, maxSize));    } catch (Exception ignored) {    }  }  public DDOkHttpDownloader(OkHttpClient client) {//---    this.client = client;  }  protected final OkHttpClient getClient() {    return client;  }  @Override public Response load(Uri uri, int networkPolicy) throws IOException {    CacheControl cacheControl = null;//com.squareup.okhttp    if (networkPolicy != 0) {      if (NetworkPolicy.isOfflineOnly(networkPolicy)) {        cacheControl = CacheControl.FORCE_CACHE;      } else {        CacheControl.Builder builder = new CacheControl.Builder();        if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {          builder.noCache();        }        if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {          builder.noStore();        }        cacheControl = builder.build();      }    }   // 注意Request.Builder()-->对应的是com.squareup.okhttp.Request    Request.Builder builder = new Request.Builder().url(uri.toString());    if (cacheControl != null) {      builder.cacheControl(cacheControl);    }    com.squareup.okhttp.Response response = client.newCall(builder.build()).execute();    int responseCode = response.code();    if (responseCode >= 300) {      response.body().close(); //父类定义的异常类      throw new ResponseException(responseCode + " " + response.message(), networkPolicy,          responseCode);    }    boolean fromCache = response.cacheResponse() != null;    ResponseBody responseBody = response.body();    return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength());  }  @Override public void shutdown() { //closing the disk cache    com.squareup.okhttp.Cache cache = client.getCache();    if (cache != null) {      try {        cache.close();      } catch (IOException ignored) {      }    }  }}

3 UrlConnectionDownloader

// cache of 2% of the total available space will be used (capped at 50MB) will automatically be// installed in the application's cache directory, when available. public class UrlConnectionDownloader implements Downloader {  static final String RESPONSE_SOURCE = "X-Android-Response-Source";  static volatile Object cache;  private static final Object lock = new Object();  private static final String FORCE_CACHE = "only-if-cached,max-age=2147483647";//2^31-1  private static final ThreadLocal<StringBuilder> CACHE_HEADER_BUILDER =      new ThreadLocal<StringBuilder>() { // ThreadLocal的使用方法!!!        @Override protected StringBuilder initialValue() {          return new StringBuilder();        }      };  private final Context context; // final  public UrlConnectionDownloader(Context context) {    this.context = context.getApplicationContext(); // 获取App的  }  protected HttpURLConnection openConnection(Uri path) throws IOException {    HttpURLConnection connection = (HttpURLConnection) new URL(path.toString()).openConnection();    connection.setConnectTimeout(Utils.DEFAULT_CONNECT_TIMEOUT_MILLIS);    connection.setReadTimeout(Utils.DEFAULT_READ_TIMEOUT_MILLIS);    return connection;  }  @Override public Response load(Uri uri, int networkPolicy) throws IOException {    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {//14      installCacheIfNeeded(context); //缓存    }    HttpURLConnection connection = openConnection(uri);    connection.setUseCaches(true);    if (networkPolicy != 0) {      String headerValue;      if (NetworkPolicy.isOfflineOnly(networkPolicy)) {        headerValue = FORCE_CACHE; // "only-if-cached,max-age=2147483647"      } else {        StringBuilder builder = CACHE_HEADER_BUILDER.get();//ThreadLocal        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);    // Returns true if header indicates the response body was loaded from the disk cache. boolean fromCache = Utils.parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));    //RESPONSE_SOURCE = "X-Android-Response-Source"    return new Response(connection.getInputStream(), fromCache, contentLength);  }  @Override public void shutdown() {    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && cache != null) {      ResponseCacheIcs.close(cache);    }  }  private static void installCacheIfNeeded(Context context) {    // DCL + volatile should be safe after Java 5.---double checked locking    if (cache == null) {      try {        synchronized (lock) {          if (cache == null)             cache = ResponseCacheIcs.install(context);        }      } catch (IOException ignored) {      }    }  }  private static class ResponseCacheIcs {    static Object install(Context context) throws IOException {      File cacheDir = Utils.createDefaultCacheDir(context);      HttpResponseCache cache = HttpResponseCache.getInstalled();      if (cache == null) {        long maxSize = Utils.calculateDiskCacheSize(cacheDir);        cache = HttpResponseCache.install(cacheDir, maxSize);      }      return cache;    }    static void close(Object cache) {      try {        ((HttpResponseCache) cache).close();      } catch (IOException ignored) {      }    }  }}

2 Cache

1 Cache接口

A memory cache

/*A memory cache for storing the most recently used images.Note: The Cache is accessed by multiple threads. You must ensure your Cache implementation is thread safe when get(String) or set(String, Bitmap) is called.*/public interface Cache {  Bitmap get(String key);    void set(String key, Bitmap bitmap);  /** Returns the current size of the cache in bytes. */  int size();  /** Returns the maximum size in bytes that the cache can hold. */  int maxSize();  void clear();  /** Remove items whose key is prefixed with {@code keyPrefix}. */  void clearKeyUri(String keyPrefix);  /** A cache which does not store any values. */  ICache NONE = new ICache() {  ...  };}

2 LruCache

A memory cache which uses a least-recently used eviction收回 policy

public class LruCache implements Cache {  final LinkedHashMap<String, Bitmap> map;  private final int maxSize;  private int size;  private int putCount;  private int evictionCount;  private int hitCount;  private int missCount;  /** Create a cache using an appropriate portion of the available RAM as the maximum size. */  public LruCache(Context context) {    this(Utils.calculateMemoryCacheSize(context));  }  /** Create a cache with a given maximum size in bytes. */  public LruCache(int maxSize) {    if (maxSize <= 0) {      throw new IllegalArgumentException("Max size must be positive.");    }    this.maxSize = maxSize;    /*  initialCapacity--the initial capacity of this hash map.        loadFactor--the initial load factor.        accessOrder--true if the ordering should be done based on the last access (from least-recently accessed to most-recently accessed从最远用到最近用), and false if the ordering should be the order in which the entries were inserted根据插入.   */    this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);  }  @Override public Bitmap get(String key) {//父类说了 thread safe    if (key == null) {      throw new NullPointerException("key == null");    }    Bitmap mapValue;    synchronized (this) {      mapValue = map.get(key);      if (mapValue != null) {        hitCount++;        return mapValue;      }      missCount++;    }    return null;  }  @Override public void set(String key, Bitmap bitmap) {    if (key == null || bitmap == null) {          throw new NullPointerException("key == null || bitmap == null");    }    Bitmap previous;    synchronized (this) {//父类说了 thread safe      putCount++;      size += Utils.getBitmapBytes(bitmap);      previous = map.put(key, bitmap);      if (previous != null) {        size -= Utils.getBitmapBytes(previous);      }    }    trimToSize(maxSize);  }  private void trimToSize(int maxSize) {    while (true) {      String key;      Bitmap value;      synchronized (this) {//thread safe        if (size < 0 || (map.isEmpty() && size != 0)) {          throw new IllegalStateException(              getClass().getName() + ".sizeOf() is reporting inconsistent results!");        }        if (size <= maxSize || map.isEmpty()) {          break;        }//现在的排序是from least-recently accessed to most-recently accessed因此先删除最近没有用的        Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();        key = toEvict.getKey();        value = toEvict.getValue();        map.remove(key);        size -= Utils.getBitmapBytes(value);        evictionCount++;      }    }  }  /** Clear the cache. */// -1 will evict 0-sized elements  public final void evictAll() {    trimToSize(-1);   }  @Override public final synchronized int size() {    return size;  }  @Override public final synchronized int maxSize() {    return maxSize;  }  @Override public final synchronized void clear() {    evictAll();  }  @Override public final synchronized void clearKeyUri(String uri) {    boolean sizeChanged = false;    int uriLength = uri.length();    for (Iterator<Map.Entry<String, Bitmap>> i = map.entrySet().iterator(); i.hasNext();) {      Map.Entry<String, Bitmap> entry = i.next();      String key = entry.getKey();      Bitmap value = entry.getValue();      int newlineIndex = key.indexOf(KEY_SEPARATOR);// char KEY_SEPARATOR = '\n';      if (newlineIndex == uriLength && key.substring(0, newlineIndex).equals(uri)) {        i.remove();        size -= Utils.getBitmapBytes(value);        sizeChanged = true;      }    }    if (sizeChanged) {      trimToSize(maxSize);    }  } }

3 Stats和StatsSnapshot

1 Stats

class Stats {  private static final int CACHE_HIT = 0;  private static final int CACHE_MISS = 1;  private static final int BITMAP_DECODE_FINISHED = 2; // decode  private static final int BITMAP_TRANSFORMED_FINISHED = 3; // TRANSFORMED  private static final int DOWNLOAD_FINISHED = 4;  private static final String STATS_THREAD_NAME = Utils.THREAD_PREFIX + "Stats";  final HandlerThread statsThread;  final ICache cache;  final Handler handler;  long cacheHits;  long cacheMisses;  long totalDownloadSize;  long totalOriginalBitmapSize;  long totalTransformedBitmapSize;  long averageDownloadSize;  long averageOriginalBitmapSize;  long averageTransformedBitmapSize;  int downloadCount;  int originalBitmapCount;  int transformedBitmapCount;  Stats(ICache cache) {    this.cache = cache;    this.statsThread = new HandlerThread(STATS_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);    this.statsThread.start();//下面语句功能:每秒发送一个消息让其一直在处理工作中,不要一直停留   Utils.flushStackLocalLeaks(statsThread.getLooper());    this.handler = new StatsHandler(statsThread.getLooper(), this);  }  void dispatchDownloadFinished(long size) {    handler.sendMessage(handler.obtainMessage(DOWNLOAD_FINISHED, size));  }  void shutdown() {    statsThread.quit();  }  void performBitmapDecoded(long size) {    originalBitmapCount++;    totalOriginalBitmapSize += size;    averageOriginalBitmapSize = getAverage(originalBitmapCount, totalOriginalBitmapSize);  }  StatsSnapshot createSnapshot() {    return new StatsSnapshot(cache.maxSize(), cache.size(), cacheHits, cacheMisses, totalDownloadSize, totalOriginalBitmapSize, totalTransformedBitmapSize, averageDownloadSize, averageOriginalBitmapSize, averageTransformedBitmapSize, downloadCount, originalBitmapCount, transformedBitmapCount, System.currentTimeMillis());  }  private static long getAverage(int count, long totalSize) {    return totalSize / count;  }  private static class StatsHandler extends Handler {    private final Stats stats;    public StatsHandler(Looper looper, Stats stats) {      super(looper);      this.stats = stats;    }    @Override public void handleMessage(final Message msg) {      switch (msg.what) {        case CACHE_HIT:          stats.performCacheHit(); //  cacheHits++;          break;        case CACHE_MISS:          stats.performCacheMiss(); //cacheMisses++;          break;        case BITMAP_DECODE_FINISHED:          stats.performBitmapDecoded(msg.arg1);          break;        case BITMAP_TRANSFORMED_FINISHED:          stats.performBitmapTransformed(msg.arg1);          break;        case DOWNLOAD_FINISHED:          stats.performDownloadFinished((Long) msg.obj);          break;        default:          Picasso.HANDLER.post(new Runnable() {            @Override public void run() {              throw new AssertionError("Unhandled stats message." + msg.what);            }          });      }    }  }}

2 StatsSnapshot

/** Represents all stats for a {@link Picasso} instance at a single point in time. */

  /** Prints out this {@link StatsSnapshot} into log. */  public void dump() {    StringWriter logWriter = new StringWriter();    dump(new PrintWriter(logWriter));    Log.i(TAG, logWriter.toString());  }  public void dump(PrintWriter writer) {    writer.println("===============BEGIN PICASSO STATS ===============");    writer.println("Memory Cache Stats");    writer.print("  Max Cache Size: ");    writer.println(maxSize);    ....}

4 Utils

final class Utils {    static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 15 * 1000; // 15s  private static final int KEY_PADDING = 50; // Determined by exact science.  private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB  private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB  static final int THREAD_LEAK_CLEANING_MS = 1000;  static final char KEY_SEPARATOR = '\n';  /** Thread confined to main thread for key creation. */  static final StringBuilder MAIN_THREAD_KEY_BUILDER = new StringBuilder();  /** Logging */  static final String OWNER_MAIN = "Main";  static final String OWNER_DISPATCHER = "Dispatcher";  static final String OWNER_HUNTER = "Hunter";  static final String VERB_CREATED = "created";  static final String VERB_CHANGED = "changed";  static final String VERB_IGNORED = "ignored";  static final String VERB_ENQUEUED = "enqueued";// 入队,排队  static final String VERB_CANCELED = "canceled";  static final String VERB_BATCHED = "batched";//批处理  static final String VERB_RETRYING = "retrying";  static final String VERB_EXECUTING = "executing";//执行  static final String VERB_DECODED = "decoded";  static final String VERB_TRANSFORMED = "transformed";  static final String VERB_JOINED = "joined";  static final String VERB_REMOVED = "removed";  static final String VERB_DELIVERED = "delivered";//交付; 交出  static final String VERB_REPLAYING = "replaying";//重演  static final String VERB_COMPLETED = "completed";  static final String VERB_ERRORED = "errored";  static final String VERB_PAUSED = "paused";  static final String VERB_RESUMED = "resumed";  // WebP格式,谷歌(google)开发的一种旨在加快图片加载速度的图片格式。  // 图片压缩体积大约只有JPEG的2/3,并能节省大量的服务器带宽资源和数据空间。  // Facebook Ebay等知名网站已经开始测试并使用WebP格式。  /* WebP file header     0                   1                   2                   3     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |      'R'      |      'I'      |      'F'      |      'F'      |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |                           File Size                           |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |      'W'      |      'E'      |      'B'      |      'P'      |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  */  private static final int WEBP_FILE_HEADER_SIZE = 12;  private static final String WEBP_FILE_HEADER_RIFF = "RIFF";  private static final String WEBP_FILE_HEADER_WEBP = "WEBP";  private Utils() {// 暗示    // No instances.  }  static int getBitmapBytes(Bitmap bitmap) {    int result;    if (SDK_INT >= HONEYCOMB_MR1) {//12      result = BitmapHoneycombMR1.getByteCount(bitmap);    } else {      result = bitmap.getRowBytes() * bitmap.getHeight();    }    if (result < 0) {      throw new IllegalStateException("Negative size: " + bitmap);    }    return result;  }  private static class BitmapHoneycombMR1 {    static int getByteCount(Bitmap bitmap) {      return bitmap.getByteCount();    }  }  static <T> T checkNotNull(T value, String message) {    if (value == null) {      throw new NullPointerException(message);    }    return value;  }  static void checkNotMain() {    if (isMain()) {      throw new IllegalStateException("Method call should not happen from the main thread.");   }  }  static boolean isMain() {          return Looper.getMainLooper().getThread() == Thread.currentThread();  }  static String getLogIdsForHunter(BitmapHunter hunter, String prefix) {    StringBuilder builder = new StringBuilder(prefix);    Action action = hunter.getAction();    if (action != null) {      builder.append(action.request.logId());    }    List<Action> actions = hunter.getActions();    if (actions != null) {      for (int i = 0, count = actions.size(); i < count; i++) {        if (i > 0 || action != null) builder.append(", ");        builder.append(actions.get(i).request.logId());      }    }    return builder.toString();  }  static void log(String owner, String verb, String logId, String extras) {    Log.d(TAG, format("%1$-11s %2$-12s %3$s %4$s", owner, verb, logId, extras));  }  static String createKey(Request data) {    String result = createKey(data, MAIN_THREAD_KEY_BUILDER);    MAIN_THREAD_KEY_BUILDER.setLength(0); //setLength    return result;  }  static String createKey(Request data, StringBuilder builder) {    if (data.stableKey != null) {//确保空间足够,不够就增加increased to the largest value of either       // the minimumCapacity or the current capacity multiplied by two plus two      builder.ensureCapacity(data.stableKey.length() + KEY_PADDING); //KEY_PADDING = 50      builder.append(data.stableKey);    } else if (data.uri != null) {      String path = data.uri.toString();      builder.ensureCapacity(path.length() + KEY_PADDING);      builder.append(path);    } else {      builder.ensureCapacity(KEY_PADDING);      builder.append(data.resourceId);    }    builder.append(KEY_SEPARATOR); //KEY_SEPARATOR = '\n'    if (data.rotationDegrees != 0) {      builder.append("rotation:").append(data.rotationDegrees);      if (data.hasRotationPivot) {        builder.append('@').append(data.rotationPivotX).append('x').append(data.rotationPivotY);      }      builder.append(KEY_SEPARATOR);    }    if (data.hasSize()) {      builder.append("resize:").append(data.targetWidth).append('x').append(data.targetHeight);      builder.append(KEY_SEPARATOR);    }    if (data.centerCrop) {      builder.append("centerCrop").append(KEY_SEPARATOR);    } else if (data.centerInside) {      builder.append("centerInside").append(KEY_SEPARATOR);    }    if (data.transformations != null) {      /*noinspection ForLoopReplaceableByForEach:noinspection is an IntelliJ specific annotation.       It's similar to Java's @SupressWarnings except that it can be used for a single statement       instead of declaring it at class or method level as @SupressWarnings. In this case, it is       It means that you're using a counter to run through the list, when you could just do:      for (Object obj : list)*/      for (int i = 0, count = data.transformations.size(); i < count; i++) {        builder.append(data.transformations.get(i).key());        builder.append(KEY_SEPARATOR);      }    }    return builder.toString();  }  // good 常见了已经  static void closeQuietly(InputStream is) {    if (is == null) return;    try {      is.close();    } catch (IOException ignored) {    }  }  /*Returns {@code true} if header indicates the response body was loaded from the disk cache. */  static boolean parseResponseSourceHeader(String header) {    /*摘自 http://zhidao.baidu.com/link?url=gzpcDl3s2gVRWccT9FYa35QHfgQEtwiPHaSekTufJSfwnbRo8kCxLIvJdNCs00cFi16G-3Fb4dWqbD4JMaoZ6txTg_Jj4SiDk2UA3ifgZrW    当浏览器第一次加载资源的时候,返回一般为200,意思是成功获取资源,并会在浏览器的缓存中记录下max-age,第二次访问的时果没有过期,则直接读缓存,根本不会和服务器进行交互,换句话说,断网都能打开,就和本地跑一样!如果已经过期了,那就去服务器请求,等待服务器响应,这是很费时间的,服务器如果发现资源没有改变过,那么就会返回304,告诉浏览器,我没变过,你去读缓存吧,于是浏览器也不用从服务器拉数据了,然而,等待服务器响应也是一个很要命的问题,在网速发达的今天,等一个响应,有时比下载还慢。如果是用浏览器刷新的,那么浏览器不会去判断max-age了,直接去服务器拿,如果服务器判断资源没变过,则还是会返回304,和上面是一样的,所以刷新一下,其实很可怕,等于把所有的资源都要去服务器请求一边,问问服务器我过期了没有。综上,尽量减少网页的资源数量!尽量合并JS CSS 图片!响应速度将会猛增!当今,响应速度比网速重要!!*/    if (header == null) {      return false;    }    String[] parts = header.split(" ", 2);    if ("CACHE".equals(parts[0])) {      return true;    }    if (parts.length == 1) {      return false;    }    try {      return "CONDITIONAL_CACHE".equals(parts[0]) && Integer.parseInt(parts[1]) == 304;    } catch (NumberFormatException e) {      return false;    }  }  static Downloader createDefaultDownloader(Context context) {    if (SDK_INT >= GINGERBREAD) { // 9        try { // 没有异常说明用户添加了Okhttp的包          Class.forName("com.squareup.okhttp.OkHttpClient");          return OkHttpLoaderCreator.create(context);        } catch (ClassNotFoundException ignored) {        }    }    return new DDUrlConnectionDownloader(context);  }  private static class OkHttpLoaderCreator {    static Downloader create(Context context) {      return new OkHttpDownloader(context);    }  }  static File createDefaultCacheDir(Context context) {    File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);    if (!cache.exists()) {      //noinspection ResultOfMethodCallIgnored      cache.mkdirs();    }    return cache;  }  static long calculateDiskCacheSize(File dir) {    long size = MIN_DISK_CACHE_SIZE;    try { // 计算SD卡缓存的大小,需要在5M和50M之间      StatFs statFs = new StatFs(dir.getAbsolutePath());      long available = ((long) statFs.getBlockCount()) * statFs.getBlockSize();      // Target 2% of the total space.占用总空间的2%      size = available / 50;    } catch (IllegalArgumentException ignored) {    }    // Bound inside min/max size for disk cache.    return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);  }  static int calculateMemoryCacheSize(Context context) {    ActivityManager am = getService(context, ACTIVITY_SERVICE);//获取服务,泛型时不用转换了    // true when the application has requested a large heap for its processes    boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;    int memoryClass = am.getMemoryClass(); // megabytes单位是MB    if (largeHeap && SDK_INT >= HONEYCOMB) { // 11      memoryClass = ActivityManagerHoneycomb.getLargeMemoryClass(am);    }    // Target ~15% of the available heap.-----------    return 1024 * 1024 * memoryClass / 7;  }  private static class ActivityManagerHoneycomb {    static int getLargeMemoryClass(ActivityManager activityManager) {      return activityManager.getLargeMemoryClass();    }  }  static boolean isAirplaneModeOn(Context context) {//飞行模式    ContentResolver contentResolver = context.getContentResolver();    try {      return Settings.System.getInt(contentResolver, AIRPLANE_MODE_ON, 0) != 0;    } catch (NullPointerException e) {      // https://github.com/square/picasso/issues/761, some devices might crash here, assume that      // airplane mode is off.      return false;    }  }  @SuppressWarnings("unchecked") //获取一个服务,利用泛型  static <T> T getService(Context context, String service) {    return (T) context.getSystemService(service);  }  static boolean hasPermission(Context context, String permission) {//是否有权限    return context.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;  }  // InputStream----byte[]  static byte[] toByteArray(InputStream input) throws IOException {    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();    byte[] buffer = new byte[1024 * 4];    int n;    while (-1 != (n = input.read(buffer))) {      byteArrayOutputStream.write(buffer, 0, n);    }    return byteArrayOutputStream.toByteArray();  }  static boolean isWebPFile(InputStream stream) throws IOException {    byte[] fileHeaderBytes = new byte[WEBP_FILE_HEADER_SIZE];//12    boolean isWebPFile = false;    if (stream.read(fileHeaderBytes, 0, WEBP_FILE_HEADER_SIZE) == WEBP_FILE_HEADER_SIZE) {      // If a file's header starts with RIFF and end with WEBP, the file is a WebP file      isWebPFile = WEBP_FILE_HEADER_RIFF.equals(new String(fileHeaderBytes, 0, 4, "US-ASCII"))          && WEBP_FILE_HEADER_WEBP.equals(new String(fileHeaderBytes, 8, 4, "US-ASCII"));    }    return isWebPFile;  }  static int getResourceId(Resources resources, Request data) throws FileNotFoundException {    if (data.resourceId != 0 || data.uri == null) {      return data.resourceId;    }    String pkg = data.uri.getAuthority();    if (pkg == null) throw new FileNotFoundException("No package provided: " + data.uri);    int id;    List<String> segments = data.uri.getPathSegments();    if (segments == null || segments.isEmpty()) {      throw new FileNotFoundException("No path segments: " + data.uri);    } else if (segments.size() == 1) {      try {        id = Integer.parseInt(segments.get(0));      } catch (NumberFormatException e) {        throw new FileNotFoundException("Last path segment is not a resource ID: " + data.uri);      }    } else if (segments.size() == 2) {      String type = segments.get(0);      String name = segments.get(1);      /**Return a resource identifier for the given resource name. A fully qualified resource name is of the form "package:type/entry". The first two components (package and type) are optional if defType and defPackage, respectively, are specified here.*/      id = resources.getIdentifier(name, type, pkg);    } else {      throw new FileNotFoundException("More than two path segments: " + data.uri);    }    return id;  }  static Resources getResources(Context context, Request data) throws FileNotFoundException {    if (data.resourceId != 0 || data.uri == null) {      return context.getResources();    }    String pkg = data.uri.getAuthority();    if (pkg == null) throw new FileNotFoundException("No package provided: " + data.uri);    try {      PackageManager pm = context.getPackageManager();      return pm.getResourcesForApplication(pkg);//---------------    } catch (PackageManager.NameNotFoundException e) {      throw new FileNotFoundException("Unable to obtain resources for package: " + data.uri);    }  }  /**Prior to 在…之前;   * Prior to Android 5, HandlerThread always keeps a stack local reference to the last message   * that was sent to it. This method makes sure that stack local reference never stays there   * for too long by sending new messages to it every second.   */  static void flushStackLocalLeaks(Looper looper) {    Handler handler = new Handler(looper) {      @Override public void handleMessage(Message msg) {        sendMessageDelayed(obtainMessage(), THREAD_LEAK_CLEANING_MS); //1000      }    };    handler.sendMessageDelayed(handler.obtainMessage(), THREAD_LEAK_CLEANING_MS);  }  // 供线程池PicassoExecutorService使用  static class PicassoThreadFactory implements ThreadFactory {    @SuppressWarnings("NullableProblems")    public Thread newThread(Runnable r) {      return new PicassoThread(r);    }  }  private static class PicassoThread extends Thread {    public PicassoThread(Runnable r) {      super(r);    }    @Override public void run() {      Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);      super.run();    }  }}
0 0
原创粉丝点击