您的位置:首页 > 其它

Picasso分析02

2015-10-02 19:41 1256 查看

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();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  picasso