Picasso 是一個強大的圖片加載緩存框架
一竿屹、使用
Picasso.with(this)
.load("url")
.placeholder(R.drawable.leak_canary_icon)//占位圖
.error(R.drawable.leak_canary_icon)//網(wǎng)絡(luò)失敗顯示的圖片
.resize(480, 480)//指定圖片的尺寸
.centerCrop()//指定圖片縮放類型
.rotate(90)
.priority(Picasso.Priority.HIGH)//指定優(yōu)先級
.tag("tag")
.memoryPolicy(MemoryPolicy.NO_CACHE)
.networkPolicy(NetworkPolicy.NO_CACHE)
.into(imageView);
二拱燃、重要的類
-
Picasso
: 圖片加載召嘶、轉(zhuǎn)換弄跌、緩存的管理類铛只。單列模式 淳玩,通過with方法獲取實例蜕着,也是加載圖片的入口蓖乘。 -
RequestCreator
: Request構(gòu)建類悄雅,Builder 模式众眨,采用鏈式設(shè)置該Request的屬性(如占位圖娩梨、緩存策略狈定、裁剪規(guī)則纽什、顯示大小、優(yōu)先級等等)让蕾。最后調(diào)用build()方法生成一個請求(Request)探孝。 -
DeferredRequestCreator
:當創(chuàng)建請求的時候還不能獲取ImageView的寬和高的時候顿颅,則創(chuàng)建一個DeferredRequestCreator,DeferredRequestCreator里對 target 設(shè)置監(jiān)聽栖疑,直到可以獲取到寬和高的時候重新執(zhí)行請求創(chuàng)建遇革。 -
Action
: 請求包裝類锻霎,存儲了該請求和RequestCreator設(shè)置的這些屬性旋恼,最終提交給線程執(zhí)行下載冰更。 -
Dispatcher
:分發(fā)器蜀细,分發(fā)執(zhí)行各種請求、分發(fā)結(jié)果等等归斤。 -
PicassoExecutorService
:Picasso使用的線程池刁岸,默認池大小為3。 -
LruCache
:一個使用最近最少使用策略的內(nèi)存緩存鸦难。 -
BitmapHunter
:這是Picasso的一個核心的類根吁,開啟線程執(zhí)行下載,獲取結(jié)果后解碼成Bitmap,然后做一些轉(zhuǎn)換操作如圖片旋轉(zhuǎn)合蔽、裁剪等击敌,如果請求設(shè)置了轉(zhuǎn)換器Transformation,也會在BitmapHunter里執(zhí)行這些轉(zhuǎn)換操作。 -
NetworkRequestHandler
:網(wǎng)絡(luò)請求處理器拴事,如果圖片需要從網(wǎng)絡(luò)下載沃斤,則用這個處理器處理。 -
FileRequestHandler
:文件請求處理器刃宵,如果請求的是一張存在文件中的圖片哮针,則用這個處理器處理缩抡。 -
AssetRequestHandler
: Asset 資源圖片處理器内边,如果是加載asset目錄下的圖片和屎,則用這個處理器處理随常。 -
ResourceRequestHandler
:Resource資源圖片處理器枣察,如果是加載res下的圖片猿涨,則用這個處理器處理红伦。 -
ContentStreamRequestHandle
r: ContentProvider 處理器唠叛,如果是ContentProvider提供的圖片障般,則用這個處理器處理 -
MediaStoreRequestHandler
: MediaStore 請求處理器定拟,如果圖片是存在MediaStore上的則用這個處理器處理延窜。
15.``Response`: 返回的結(jié)果信息商源,Stream流或者Bitmap缎除。 -
Request
: 請求實體類铸董,存儲了應(yīng)用在圖片上的信息颤芬。 -
Target
:圖片加載的監(jiān)聽器接口夺艰,有3個回調(diào)方法肥隆,onPrepareLoad 在請求提交前回調(diào)矾屯,onBitmapLoaded 請求成功回調(diào)亚情,并返回Bitmap,onBitmapFailed請求失敗回調(diào)栅迄。 -
PicassoDrawable
:繼承BitmapDrawable,實現(xiàn)了過渡動畫和圖片來源的標識(就是圖片來源的指示器泳叠,要調(diào)用 setIndicatorsEnabled(true)方法才生效),請求成功后都會包裝成BitmapDrawable顯示到ImageView 上锅棕。 -
OkHttpDownloader
:用OkHttp實現(xiàn)的圖片下載器移稳,默認就是用的這個下載器都许。 -
UrlConnectionDownloader
:使用HttpURLConnection 實現(xiàn)的下載器。 -
MemoryPolicy
: 內(nèi)存緩存策略暇昂,一個枚舉類型阱扬。 -
NetworkPolicy
: 磁盤緩存策略匈辱,一個枚舉類型焚虱。 -
Stats
: 這個類相當于日志記錄购裙,會記錄如:內(nèi)存緩存的命中次數(shù),丟失次數(shù)鹃栽,下載次數(shù)躏率,轉(zhuǎn)換次數(shù)等等,我們可以通過StatsSnapshot類將日志打印出來民鼓,看一下整個項目的圖片加載情況薇芝。
三、源碼分析
1丰嘉、獲取Picasso實例
Picasso采用單例模式
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}
// 真正new 的地方在build()方法里
public Picasso build() {
Context context = this.context;
if (downloader == null) {
//1. 配置默認的下載器夯到,首先通過反射獲取OkhttpClient,如果獲取到了,就使用OkHttpDwownloader作為默認下載器
//如果獲取不到就使用UrlConnectionDownloader作為默認下載器
downloader = Utils.createDefaultDownloader(context);
}
if (cache == null) {
// 2. 配置內(nèi)存緩存饮亏,大小為手機內(nèi)存的15%
cache = new LruCache(context);
}
if (service == null) {
// 3.配置Picaso 線程池耍贾,核心池大小為3
service = new PicassoExecutorService();
}
if (transformer == null) {
// 4. 配置請求轉(zhuǎn)換器,默認的請求轉(zhuǎn)換器沒有做任何事路幸,直接返回原請求
transformer = RequestTransformer.IDENTITY;
}
Stats stats = new Stats(cache);
//5.創(chuàng)建分發(fā)器
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
- 配置默認的下載器
static Downloader createDefaultDownloader(Context context) {
try {
// 查找項目中是否使用了OkHttp
Class.forName("com.squareup.okhttp.OkHttpClient");
return OkHttpLoaderCreator.create(context);
} catch (ClassNotFoundException ignored) {
}
// 沒有使用OkHttp則用默認的HttpURLConnection
return new UrlConnectionDownloader(context);
}
- 配置內(nèi)存緩存:最多手機內(nèi)存的15%
public LruCache(Context context) {
this(Utils.calculateMemoryCacheSize(context));
}
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("Max size must be positive.");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
}
static int calculateMemoryCacheSize(Context context) {
ActivityManager am = getService(context, ACTIVITY_SERVICE);
boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
int memoryClass = am.getMemoryClass();
if (largeHeap && SDK_INT >= HONEYCOMB) {
memoryClass = am.getLargeMemoryClass();
}
// Target ~15% of the available heap.
return 1024 * 1024 * memoryClass / 7;
}
- 配置線程池
PicassoExecutorService() {
super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
}
- 配置默認請求轉(zhuǎn)換器
// 空轉(zhuǎn)換荐开,請求原樣返回
RequestTransformer IDENTITY = new RequestTransformer() {
@Override public Request transformRequest(Request request) {
return request;
}
};
- Dispatcher分發(fā)器
Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
Downloader downloader, Cache cache, Stats stats) {
// DispatcherThread繼承自HandlerThread,主要做一些耗時操作
this.dispatcherThread = new DispatcherThread();
this.dispatcherThread.start();
this.context = context;
this.service = service;
this.hunterMap = new LinkedHashMap<String, BitmapHunter>();
this.failedActions = new WeakHashMap<Object, Action>();
this.pausedActions = new WeakHashMap<Object, Action>();
this.pausedTags = new HashSet<Object>();
// DispatcherThread子線程的handler
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
this.downloader = downloader;
// 主線程handler
this.mainThreadHandler = mainThreadHandler;
this.cache = cache;
this.stats = stats;
this.batch = new ArrayList<BitmapHunter>(4);
this.airplaneMode = Utils.isAirplaneModeOn(this.context);
this.scansNetworkChanges = hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);
// 監(jiān)聽網(wǎng)絡(luò)變化
this.receiver = new NetworkBroadcastReceiver(this);
receiver.register();
}
NetworkBroadcastReceiver
監(jiān)聽網(wǎng)絡(luò)變化
static class NetworkBroadcastReceiver extends BroadcastReceiver {
static final String EXTRA_AIRPLANE_STATE = "state";
private final Dispatcher dispatcher;
NetworkBroadcastReceiver(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
@Override public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
if (!intent.hasExtra(EXTRA_AIRPLANE_STATE)) {
return;
}
dispatcher.dispatchAirplaneModeChange(intent.getBooleanExtra(EXTRA_AIRPLANE_STATE, false));
} else if (CONNECTIVITY_ACTION.equals(action)) {
ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
// 監(jiān)聽到網(wǎng)絡(luò)變化后简肴,dispatcher做響應(yīng)的操作
dispatcher.dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
}
}
}
- 監(jiān)聽到網(wǎng)絡(luò)變化后晃听,dispatcher做響應(yīng)的操作
void performNetworkStateChange(NetworkInfo info) {
if (service instanceof PicassoExecutorService) {
// dispatcher 會根據(jù)網(wǎng)絡(luò)狀態(tài)調(diào)整線程池
((PicassoExecutorService) service).adjustThreadCount(info);
}
// Intentionally check only if isConnected() here before we flush out failed actions.
if (info != null && info.isConnected()) {
flushFailedActions();
}
}
// 根據(jù)網(wǎng)絡(luò)狀態(tài)調(diào)整線程池
void adjustThreadCount(NetworkInfo info) {
if (info == null || !info.isConnectedOrConnecting()) {
setThreadCount(DEFAULT_THREAD_COUNT);
return;
}
switch (info.getType()) {
case ConnectivityManager.TYPE_WIFI:
case ConnectivityManager.TYPE_WIMAX:
case ConnectivityManager.TYPE_ETHERNET:
setThreadCount(4);
break;
case ConnectivityManager.TYPE_MOBILE:
switch (info.getSubtype()) {
case TelephonyManager.NETWORK_TYPE_LTE: // 4G
case TelephonyManager.NETWORK_TYPE_HSPAP:
case TelephonyManager.NETWORK_TYPE_EHRPD:
setThreadCount(3);
break;
case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
case TelephonyManager.NETWORK_TYPE_CDMA:
case TelephonyManager.NETWORK_TYPE_EVDO_0:
case TelephonyManager.NETWORK_TYPE_EVDO_A:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
setThreadCount(2);
break;
case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
case TelephonyManager.NETWORK_TYPE_EDGE:
setThreadCount(1);
break;
default:
setThreadCount(DEFAULT_THREAD_COUNT);
}
break;
default:
setThreadCount(DEFAULT_THREAD_COUNT);
}
}
private void setThreadCount(int threadCount) {
setCorePoolSize(threadCount);
setMaximumPoolSize(threadCount);
}
2、load生成RequestCreator
通過load方法生成一個RequestCreator,用鏈式api 來構(gòu)建一個圖片下載請求
public RequestCreator load(String path) {
if (path == null) {
return new RequestCreator(this, null, 0);
}
if (path.trim().length() == 0) {
throw new IllegalArgumentException("Path must not be empty.");
}
return load(Uri.parse(path));
}
RequestCreator提供了很多的API 來構(gòu)建請求能扒,如展位圖佣渴、大小、轉(zhuǎn)換器赫粥、裁剪等等,這些API其實是為對應(yīng)的屬性賦值,最終會傳入into方法中構(gòu)建請求予借。
public class RequestCreator {
private static final AtomicInteger nextId = new AtomicInteger();
private final Picasso picasso;
private final Request.Builder data;
private boolean noFade;
private boolean deferred;
private boolean setPlaceholder = true;
private int placeholderResId;
private int errorResId;
private int memoryPolicy;
private int networkPolicy;
private Drawable placeholderDrawable;
private Drawable errorDrawable;
private Object tag;
}
3越平、into添加View,并請求下載
into方法里面干了3件事情:
判斷是否設(shè)置了fit 屬性灵迫,如果設(shè)置了秦叛,再看是否能夠獲取ImageView 的寬高,如果獲取不到瀑粥,生成一個DeferredRequestCreator(延遲的請求管理器)挣跋,然后直接return,在DeferredRequestCreator中當監(jiān)聽到可以獲取ImageView 的寬高的時候,再執(zhí)行into方法狞换。
判斷是否從內(nèi)存緩存獲取圖片避咆,如果沒有設(shè)置NO_CACHE,則從內(nèi)存獲取,命中直接回調(diào)CallBack 并且顯示圖片修噪。
如果緩存未命中查库,則生成一個Action,并提交Action黄琼。
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
// 檢查是否在主線程
checkMain();
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
//如果沒有url或者resourceId 則取消請求
if (!data.hasImage()) {
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
//判斷是否設(shè)置了fit屬性
if (deferred) {
if (data.hasSize()) {
throw new IllegalStateException("Fit cannot be used with resize.");
}
int width = target.getWidth();
int height = target.getHeight();
if (width == 0 || height == 0) {
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
//如果獲取不到寬高樊销,生成一個DeferredRequestCreator(延遲的請求管理器),然后直接return,
//在DeferredRequestCreator中當監(jiān)聽到可以獲取ImageView 的寬高的時候脏款,再執(zhí)行into方法围苫。
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
return;
}
data.resize(width, height);
}
Request request = createRequest(started);
String requestKey = createKey(request);
//是否從內(nèi)存緩存中獲取
if (shouldReadFromMemoryCache(memoryPolicy)) {
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
//緩存命中,取消請求撤师,并顯示圖片
picasso.cancelRequest(target);
setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
if (picasso.loggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
}
if (callback != null) {
callback.onSuccess();
}
return;
}
}
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
//內(nèi)存緩存未命中或者設(shè)置了不從內(nèi)存緩存獲取剂府,則生成一個Action ,提交執(zhí)行剃盾。
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);// 提交請求
}
request包裝類Action
abstract class Action<T> {
static class RequestWeakReference<M> extends WeakReference<M> {
final Action action;
public RequestWeakReference(Action action, M referent, ReferenceQueue<? super M> q) {
super(referent, q);
this.action = action;
}
}
final Picasso picasso;
final Request request;
final WeakReference<T> 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;
Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
this.picasso = picasso;
this.request = request;
this.target =
target == null ? null : new RequestWeakReference<T>(this, target, picasso.referenceQueue);
this.memoryPolicy = memoryPolicy;
this.networkPolicy = networkPolicy;
this.noFade = noFade;
this.errorResId = errorResId;
this.errorDrawable = errorDrawable;
this.key = key;
this.tag = (tag != null ? tag : this);
}
}
picasso.enqueueAndSubmit(action)
提交請求
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);
//將action 保存到了一個Map 中周循,目標View作為key
targetToAction.put(target, action);
}
submit(action);
}
// 交給分發(fā)器分發(fā)提交請求
void submit(Action action) {
dispatcher.dispatchSubmit(action);
}
Dispatcher:
void performSubmit(Action action, boolean dismissFailed) {
// 先查看保存暫停tag表里面沒有包含Action的tag,如果包含,則將Action 存到暫停Action表里
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;
}
// 如果線程池北shutDown,直接return
if (service.isShutdown()) {
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
}
return;
}
// 為請求生成一個BitmapHunter
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
hunter.future = service.submit(hunter);//提交執(zhí)行
hunterMap.put(action.getKey(), hunter);
if (dismissFailed) {
failedActions.remove(action.getTarget());
}
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
}
}
執(zhí)行請求提交
- 先查看保存暫停tag表里面沒有包含Action的tag,如果包含万俗,則將Action 存到暫停Action表里
- 從BitmapHunter表里查找有沒有對應(yīng)action的hunter,如果有直接attach
- 為這個請求生成一個BitmapHunter湾笛,提交給線程池執(zhí)行
4、BitmapHunter 下載圖片
BitmapHunter繼承Runnable闰歪,其實就是開啟一個線程執(zhí)行最終的下載
class BitmapHunter implements Runnable {
@Override public void run() {
try {
updateThreadName(data);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
}
// 調(diào)用hunt() 方法獲取最終結(jié)果
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);
}
}
}
當將一個bitmapHunter submit 給一個線程池執(zhí)行的時候嚎研,就會執(zhí)行run() 方法,run里面調(diào)用的是hunt方法來獲取結(jié)果,看一下hunt
方法:
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
// 是否從內(nèi)存緩存獲取Bitmap
if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
}
return bitmap;
}
}
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
// 請求處理器處理請求临扮,獲取結(jié)果论矾,Result里可能是Bitmap,可能是Stream
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
loadedFrom = result.getLoadedFrom();
exifRotation = result.getExifOrientation();
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) {
//如果需要做轉(zhuǎn)換杆勇,則在這里做轉(zhuǎn)換處理贪壳,如角度旋轉(zhuǎn),裁剪等蚜退。
bitmap = transformResult(data, bitmap, exifRotation);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
}
}
if (data.hasCustomTransformations()) {
// 如果配置了自定義轉(zhuǎn)換器闰靴,則在這里做轉(zhuǎn)換處理。
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;
}
5钻注、Downloader 下載器下載圖片
hunt
方法獲取結(jié)果的時候蚂且,最終調(diào)用的是配置的處理器的load
方法
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
如果是網(wǎng)絡(luò)圖片會調(diào)用NetworkRequestHandler
的load
方法
@Override public Result load(Request request, int networkPolicy) throws IOException {
//最終調(diào)用downloader的load方法獲取結(jié)果
Response response = downloader.load(request.uri, request.networkPolicy);
if (response == null) {
return null;
}
Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
Bitmap bitmap = response.getBitmap();
if (bitmap != null) {
return new Result(bitmap, loadedFrom);
}
InputStream is = response.getInputStream();
if (is == null) {
return null;
}
return new Result(is, loadedFrom);
}
etworkRequestHandler最終是調(diào)用的downloader 的load方法下載圖片。內(nèi)置了2個Downloader幅恋,OkhttpDownloader和UrlConnectionDownloader 杏死。我們以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));
// 最后獲取InputStream流包裝成Response返回
return new Response(connection.getInputStream(), fromCache, contentLength);
}
6捆交、完成加載
在BitmapHunter獲取結(jié)果后淑翼,分發(fā)器分發(fā)結(jié)果,通過Hander處理后,執(zhí)行performComplete方法:
result = hunt();
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
Dispatcher:
void performComplete(BitmapHunter hunter) {
// 這里將結(jié)果緩存到內(nèi)存
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
cache.set(hunter.getKey(), hunter.getResult());
}
/ 請求完畢,將hunter從表中移除
hunterMap.remove(hunter.getKey());
batch(hunter);
}
然后將BitmapHunter添加到一個批處理列表,通過Hander發(fā)送一個批處理消息
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
最后執(zhí)行performBatchComplete 方法绢要,通過主線程的Handler送處理完成的消息
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
最后在Picasso 中handleMessage,顯示圖片
static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case HUNTER_BATCH_COMPLETE: {
@SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = batch.size(); i < n; i++) {
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter);
}
break;
}
//后面代碼省略
...
};
回調(diào)到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!\n%s", this));
}
ImageView target = this.target.get();
if (target == null) {
return;
}
Context context = picasso.context;
boolean indicatorsEnabled = picasso.indicatorsEnabled;
//將結(jié)果包裝成一個PicassoDrawable 并顯示
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
if (callback != null) {
callback.onSuccess(); 回調(diào)callback
}
}