上一系列我們講述了Glide的簡(jiǎn)單使用和其源碼解析,還沒看的同學(xué)可以移步去瀏覽:
Glide4.9圖片框架(一)之加載圖片的常規(guī)使用方式
講完了Glide及其源碼,我們繼續(xù)看看同樣作為圖片加載框架的Picasso,內(nèi)部源碼就是怎樣的呢,這里就不去分析它的使用了,因?yàn)楹虶lide如出一轍,我們主要還是看內(nèi)部源碼的加載流程,本次源碼版本為2.71828。
Picasso的簡(jiǎn)單使用
Picasso.get()
//相當(dāng)于glide的with方法,初始化Picasso
.load("url")
//傳入圖片url,構(gòu)建requestCreater
.placeholder(R.drawable.error)
//設(shè)置占位圖
.error(R.drawable.error)
//設(shè)置加載錯(cuò)誤占位圖
.resize(480,800)
//設(shè)置加載圖片的寬高
.centerCrop()
//設(shè)置scyletype
.rotate(90)
//設(shè)置旋轉(zhuǎn)90度
.priority(Picasso.Priority.HIGH)
//加載的優(yōu)先級(jí),不是決定性的因素
.tag("list view")
//給加載添加tag
.memoryPolicy(MemoryPolicy.NO_CACHE)
//設(shè)置沒有內(nèi)存緩存
.into(imageview);
Picasso.get()
public static Picasso get() {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
if (PicassoProvider.context == null) {
throw new IllegalStateException("context == null");
}
singleton = new Builder(PicassoProvider.context).build();
}
}
}
return singleton;
}
get方法采用的雙重檢查鎖實(shí)現(xiàn)的單例,其中對(duì)PicassoProvider.context 判空,這里的context是什么呢,我們跟進(jìn)去看一下:
public final class PicassoProvider extends ContentProvider {
@SuppressLint("StaticFieldLeak") static Context context;
@Override public boolean onCreate() {
context = getContext();
return true;
}
@Nullable @Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
@Nullable @Override public String getType(@NonNull Uri uri) {
return null;
}
@Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override public int delete(@NonNull Uri uri, @Nullable String selection,
@Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
@Nullable String[] selectionArgs) {
return 0;
}
}
可以看到創(chuàng)建這個(gè)contentprovider就是為了獲取context,從而不需要從get方法里每次再傳入context,那么回到上面的get方法,我們繼續(xù)看singleton = new Builder(PicassoProvider.context).build()中的Builder構(gòu)造方法:
public Builder(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
這里是直接獲取ApplicationContext,因此圖片的加載是跟application綁定的。繼續(xù)看build()方法:
public Picasso build() {
Context context = this.context;
//創(chuàng)建下載器,這里直接封裝了OkHttp3的請(qǐng)求框架作為下載器
if (downloader == null) {
downloader = new OkHttp3Downloader(context);
}
// 創(chuàng)建LruCache緩存
if (cache == null) {
cache = new LruCache(context);
}
//創(chuàng)建線程池
if (service == null) {
service = new PicassoExecutorService();
}
//創(chuàng)建請(qǐng)求轉(zhuǎn)換器,默認(rèn)沒有添加任何轉(zhuǎn)換則直接請(qǐng)求
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}
Stats stats = new Stats(cache);
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
build()之一 OkHttp3Downloader
public OkHttp3Downloader(final Context context) {
this(Utils.createDefaultCacheDir(context));
}
static File createDefaultCacheDir(Context context) {
File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);
if (!cache.exists()) {
//noinspection ResultOfMethodCallIgnored
cache.mkdirs();
}
return cache;
}
public OkHttp3Downloader(final File cacheDir) {
this(cacheDir, Utils.calculateDiskCacheSize(cacheDir));
}
static long calculateDiskCacheSize(File dir) {
long size = MIN_DISK_CACHE_SIZE;
try {
StatFs statFs = new StatFs(dir.getAbsolutePath());
//noinspection deprecation
long blockCount =
SDK_INT < JELLY_BEAN_MR2 ? (long) statFs.getBlockCount() : statFs.getBlockCountLong();
//noinspection deprecation
long blockSize =
SDK_INT < JELLY_BEAN_MR2 ? (long) statFs.getBlockSize() : statFs.getBlockSizeLong();
long available = blockCount * blockSize;
// Target 2% of the total space.
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);
}
public OkHttp3Downloader(final File cacheDir, final long maxSize) {
this(new OkHttpClient.Builder().cache(new Cache(cacheDir, maxSize)).build());
sharedClient = false;
}
先看OkHttp3Downloader的構(gòu)造方法,這里的OkHttp3Downloader是默認(rèn)基于okhttp封裝的請(qǐng)求下載器,我們看下createDefaultCacheDir方法,根據(jù)命名其實(shí)可以猜出這個(gè)方法就是創(chuàng)建默認(rèn)的緩存文件的目錄,而這個(gè)目錄的路徑就是context.getApplicationContext().getCacheDir(),然后回到OkHttp3Downloader的this方法,繼續(xù)跟下去調(diào)用了calculateDiskCacheSize方法并把緩存目錄傳進(jìn)去,跟進(jìn)去看,這個(gè)方法就是計(jì)算默認(rèn)緩存文件的大小,通過(guò)獲取本地存儲(chǔ)空間來(lái)計(jì)算能緩存的最大文件空間長(zhǎng)度是多少。然后繼續(xù)看最下面的OkHttp3Downloader的this方法,通過(guò)新建OkHttpClient的Builder設(shè)置緩存的目錄和size并完成初始化。
build()之二 LruCache內(nèi)存緩存
public final class LruCache implements Cache {
final android.util.LruCache<String, LruCache.BitmapAndSize> cache;
/** Create a cache using an appropriate portion of the available RAM as the maximum size. */
public LruCache(@NonNull Context context) {
this(Utils.calculateMemoryCacheSize(context));
}
0
/** Create a cache with a given maximum size in bytes. */
public LruCache(int maxByteCount) {
cache = new android.util.LruCache<String, LruCache.BitmapAndSize>(maxByteCount) {
@Override protected int sizeOf(String key, BitmapAndSize value) {
return value.byteCount;
}
};
}
static int calculateMemoryCacheSize(Context context) {
ActivityManager am = getService(context, ACTIVITY_SERVICE);
boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
int memoryClass = largeHeap ? am.getLargeMemoryClass() : am.getMemoryClass();
// Target ~15% of the available heap.
return (int) (1024L * 1024L * memoryClass / 7);
}
這里我們還是只看簡(jiǎn)單的構(gòu)造方法吧,其實(shí)內(nèi)部持有了一個(gè)LruCache引用,相當(dāng)于自己封裝了一層LruCache,通過(guò)calculateMemoryCacheSize方法,計(jì)算能此初始化的內(nèi)存緩存的大小并完成LruCache初始化。這里是獲取app能用緩存的七分之一大小設(shè)置內(nèi)存緩存,關(guān)于LRUCache后面我們會(huì)單獨(dú)拿出來(lái)分析源碼,只要理解作為最近最少的使用的緩存優(yōu)先會(huì)被回收就行了,我們接著看下面:
build()之三 PicassoExecutorService線程池
if (service == null) {
service = new PicassoExecutorService();
}
class PicassoExecutorService extends ThreadPoolExecutor {
private static final int DEFAULT_THREAD_COUNT = 3;
PicassoExecutorService() {
super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
}
}
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
throw new RuntimeException("Stub!");
}
下面初始化了一個(gè)自定義的線程池PicassoExecutorService,繼承的還是自帶的線程池ThreadPoolExecutor,那么構(gòu)造方法里傳入的五個(gè)參數(shù)分別表示什么意思呢
- corePoolSize
表示默認(rèn)初始化的核心線程數(shù) - maximumPoolSize
表示線程池能創(chuàng)建的最大線程個(gè)數(shù),超過(guò)則不會(huì)繼續(xù)創(chuàng)建 - keepAliveTime
線程最大空閑時(shí)間 - TimeUnit
時(shí)間單位 - BlockingQueue
線程等待隊(duì)列 - ThreadFactory
線程創(chuàng)建工廠
build()之四 Dispatcher
class Dispatcher {
Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
Downloader downloader, Cache cache, Stats stats) {
this.dispatcherThread = new DispatcherThread();
this.context = context;
this.service = service;
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
this.downloader = downloader;
this.mainThreadHandler = mainThreadHandler;
this.cache = cache;
this.stats = stats;
this.receiver = new NetworkBroadcastReceiver(this);
receiver.register();
}
Dispatcher是一個(gè)比較重要的類,這里只截出了幾個(gè)比較關(guān)鍵的參數(shù)信息,dispatcher里包含了緩存信息,請(qǐng)求結(jié)果的調(diào)度,狀態(tài)的切換,其中最重要的兩個(gè)handler,DispatcherHandler和mainThreadHandler ,我們來(lái)看看內(nèi)部的消息的處理是怎么樣的:
private static class DispatcherHandler extends Handler {
private final Dispatcher dispatcher;
DispatcherHandler(Looper looper, Dispatcher dispatcher) {
super(looper);
this.dispatcher = dispatcher;
}
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
}
case REQUEST_CANCEL: {
Action action = (Action) msg.obj;
dispatcher.performCancel(action);
break;
}
case TAG_PAUSE: {
Object tag = msg.obj;
dispatcher.performPauseTag(tag);
break;
}
case TAG_RESUME: {
Object tag = msg.obj;
dispatcher.performResumeTag(tag);
break;
}
case HUNTER_COMPLETE: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performComplete(hunter);
break;
}
case HUNTER_RETRY: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performRetry(hunter);
break;
}
case HUNTER_DECODE_FAILED: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performError(hunter, false);
break;
}
case HUNTER_DELAY_NEXT_BATCH: {
dispatcher.performBatchComplete();
break;
}
case NETWORK_STATE_CHANGE: {
NetworkInfo info = (NetworkInfo) msg.obj;
dispatcher.performNetworkStateChange(info);
break;
}
case AIRPLANE_MODE_CHANGE: {
dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON);
break;
}
default:
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new AssertionError("Unknown handler message received: " + msg.what);
}
});
}
}
}
可以看到DispatcherHandler里面全是對(duì)各種狀態(tài)和結(jié)果的回調(diào)和處理,DispatcherHandler的構(gòu)造函數(shù)傳入的是dispatcherThread.getLooper(),那繼續(xù)看看這個(gè)dispatcherThread是什么意思:
static class DispatcherThread extends HandlerThread {
DispatcherThread() {
super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
}
}
其實(shí)就是繼承的HandlerThread,這里不去細(xì)看,dispatcher的構(gòu)造方法中還有一個(gè)this.receiver = new NetworkBroadcastReceiver(this).那我們重點(diǎn)看下這個(gè)廣播接收者的onReceive方法:
@Override public void onReceive(Context context, Intent intent) {
if (intent == null) {
return;
}
final String action = intent.getAction();
if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
if (!intent.hasExtra(EXTRA_AIRPLANE_STATE)) {
return; // No airplane state, ignore it. Should we query Utils.isAirplaneModeOn?
}
dispatcher.dispatchAirplaneModeChange(intent.getBooleanExtra(EXTRA_AIRPLANE_STATE, false));
} else if (CONNECTIVITY_ACTION.equals(action)) {
ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
dispatcher.dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
}
}
其實(shí)在接收廣播中可以看到對(duì)飛行模式進(jìn)行了監(jiān)聽,ACTION_AIRPLANE_MODE_CHANGED,如果是飛行模式,就調(diào)用dispatcher.dispatchAirplaneModeChange方法,跟進(jìn)去看一下:
void dispatchAirplaneModeChange(boolean airplaneMode) {
handler.sendMessage(handler.obtainMessage(AIRPLANE_MODE_CHANGE,
airplaneMode ? AIRPLANE_MODE_ON : AIRPLANE_MODE_OFF, 0));
}
void performAirplaneModeChange(boolean airplaneMode) {
this.airplaneMode = airplaneMode;
}
這里最終就是記錄一個(gè)是否為飛行模式的布爾值,我們繼續(xù)看下面的判讀網(wǎng)絡(luò)狀態(tài)鏈接的部分,dispatcher.dispatchNetworkStateChange:
void performNetworkStateChange(NetworkInfo info) {
if (service instanceof PicassoExecutorService) {
((PicassoExecutorService) service).adjustThreadCount(info);
}
if (info != null && info.isConnected()) {
flushFailedActions();
}
}
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);
}
}
這里重點(diǎn)分析一下adjustThreadCount方法,當(dāng)網(wǎng)絡(luò)狀態(tài)改變的時(shí)候,根據(jù)當(dāng)前網(wǎng)絡(luò)的類型,創(chuàng)建不同的核心線程數(shù),因?yàn)榫W(wǎng)絡(luò)的影響,可能線程池的核心線程數(shù)也受到影響,這個(gè)設(shè)計(jì)是相當(dāng)有意思的
,也是Glide中沒有去做的東西,對(duì)網(wǎng)絡(luò)差的圖片加載來(lái)說(shuō),提高了加載的效率。繼續(xù)看flushFailedActions();方法:
private void flushFailedActions() {
if (!failedActions.isEmpty()) {
Iterator<Action> iterator = failedActions.values().iterator();
while (iterator.hasNext()) {
Action action = iterator.next();
iterator.remove();
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_REPLAYING, action.getRequest().logId());
}
performSubmit(action, false);
}
}
}
failedActions這里存儲(chǔ)的是第一次請(qǐng)求失敗需要重新請(qǐng)求的集合,那么這里是空的,我們不繼續(xù)往下分析了,到這里整個(gè)Picasso的build方法就結(jié)束了,我們繼續(xù)往下分析load方法:
Picasso.load()
public RequestCreator load(@Nullable Uri uri) {
return new RequestCreator(this, uri, 0);
}
public RequestCreator load(@Nullable 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));
}
public RequestCreator load(@NonNull File file) {
if (file == null) {
return new RequestCreator(this, null, 0);
}
return load(Uri.fromFile(file));
}
public RequestCreator load(@DrawableRes int resourceId) {
if (resourceId == 0) {
throw new IllegalArgumentException("Resource ID must not be zero.");
}
return new RequestCreator(this, null, resourceId);
}
Picasso的load方法,相對(duì)于Glide來(lái)說(shuō)比較少,只有四個(gè),分別是Uri、path、file、Drawable。Uri、path、file最中都是調(diào)用的load(@Nullable Uri uri) 方法,Drawable也簡(jiǎn)單,這里我們就看傳入字符串的情況。最終調(diào)用的new RequestCreator(this, uri, 0)方法,我們跟進(jìn)去看RequestCreator主要是有什么作用:
RequestCreator
RequestCreator(Picasso picasso, Uri uri, int resourceId) {
if (picasso.shutdown) {
throw new IllegalStateException(
"Picasso instance already shut down. Cannot submit new requests.");
}
this.picasso = picasso;
this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}
Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {
this.uri = uri;
this.resourceId = resourceId;
this.config = bitmapConfig;
}
看代碼才發(fā)現(xiàn)RequestCreator其實(shí)是request的build的包裝類,由它生成request的build類,然后build才能創(chuàng)建request,還有一個(gè)作用就是RequestCreator這里包裝了一系列的鏈?zhǔn)秸{(diào)用,包括placeholder、error、fit、centerCrop等等。到這里load方法就結(jié)束了,最終我們直接看into方法:
RequestCreator.into(ImageView)
public void into(ImageView target) {
into(target, null);
}
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
checkMain();
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
if (!data.hasImage()) {
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
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());
}
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
return;
}
data.resize(width, height);
}
Request request = createRequest(started);
String requestKey = createKey(request);
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());
}
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);
}
boolean hasImage() {
return uri != null || resourceId != 0;
}
public RequestCreator fit() {
deferred = true;
return this;
}
boolean hasSize() {
return targetWidth != 0 || targetHeight != 0;
}
checkMain是檢查時(shí)否在主線程,不是則拋出異常,我們直接看data.hasImage()方法,這里的data就是我們load時(shí)創(chuàng)建的request的build,而hasImage就是判讀當(dāng)前加載的資源是不是空,如果是空則調(diào)用picasso.cancelRequest取消請(qǐng)求并且設(shè)置占位圖直接return。
接下來(lái)看deferred變量,這里只有我們?cè)O(shè)置了fit才會(huì)去執(zhí)行,那么假設(shè)我們?cè)O(shè)了fit,data.hasSize實(shí)際上是判斷我們是否設(shè)置過(guò)resize方法,如果設(shè)置了,那么不能再調(diào)用fit方法,因?yàn)檫@兩者是沖突的。我們繼續(xù)看通過(guò)target也就是傳入的image view獲取寬和高,如果獲取到的寬或者高有一個(gè)為0,表示imageview還沒能繪制成功,這里設(shè)置占位圖,并且調(diào)用picasso.defer方法,我們跟進(jìn)去看一下:
class DeferredRequestCreator implements OnPreDrawListener, OnAttachStateChangeListener {
private final RequestCreator creator;
@VisibleForTesting final WeakReference<ImageView> target;
@VisibleForTesting Callback callback;
DeferredRequestCreator(RequestCreator creator, ImageView target, Callback callback) {
this.creator = creator;
this.target = new WeakReference<>(target);
this.callback = callback;
target.addOnAttachStateChangeListener(this);
if (target.getWindowToken() != null) {
onViewAttachedToWindow(target);
}
}
@Override public void onViewAttachedToWindow(View view) {
view.getViewTreeObserver().addOnPreDrawListener(this);
}
@Override public void onViewDetachedFromWindow(View view) {
view.getViewTreeObserver().removeOnPreDrawListener(this);
}
@Override public boolean onPreDraw() {
ImageView target = this.target.get();
if (target == null) {
return true;
}
ViewTreeObserver vto = target.getViewTreeObserver();
if (!vto.isAlive()) {
return true;
}
int width = target.getWidth();
int height = target.getHeight();
if (width <= 0 || height <= 0) {
return true;
}
target.removeOnAttachStateChangeListener(this);
vto.removeOnPreDrawListener(this);
this.target.clear();
this.creator.unfit().resize(width, height).into(target, callback);
return true;
}
這里其實(shí)大致看出來(lái)DeferredRequestCreator 實(shí)現(xiàn)了OnPreDrawListener和OnAttachStateChangeListener ,那么就是在image view繪制成功后,重新再去獲取寬和高,并且調(diào)用creator.unfit().resize反法重新給build也就是data傳遞了新的圖片寬高。并將這個(gè)DeferredRequestCreator存儲(chǔ)到targetToDeferredRequestCreator集合中。那么回到上面的into方法,如果沒有設(shè)置或fit,則開始調(diào)用Request request = createRequest(started)創(chuàng)建一個(gè)請(qǐng)求,我們跟進(jìn)去看是如何創(chuàng)建的。
createRequest()
private Request createRequest(long started) {
int id = nextId.getAndIncrement();
Request request = data.build();
request.id = id;
request.started = started;
boolean loggingEnabled = picasso.loggingEnabled;
if (loggingEnabled) {
log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());
}
Request transformed = picasso.transformRequest(request);
if (transformed != request) {
// If the request was changed, copy over the id and timestamp from the original.
transformed.id = id;
transformed.started = started;
if (loggingEnabled) {
log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);
}
}
return transformed;
}
public Request build() {
......
if (priority == null) {
priority = Priority.NORMAL;
}
return new Request(uri, resourceId, stableKey, transformations, targetWidth, targetHeight,
centerCrop, centerInside, centerCropGravity, onlyScaleDown, rotationDegrees,
rotationPivotX, rotationPivotY, hasRotationPivot, purgeable, config, priority);
}
這里調(diào)用了data.build()創(chuàng)建Request,我們進(jìn)去看一下,直看重點(diǎn),直接new了一個(gè)Request對(duì)象,傳入了很多參數(shù),比如uri、資源id、key、轉(zhuǎn)換、寬高、旋轉(zhuǎn)、scaletype、優(yōu)先級(jí)等。然后回到build方法,調(diào)用了transformRequest方法,其實(shí)如果沒有設(shè)置transform,這里等于沒有變化,所以回到createRequest方法,通過(guò)createKey(request)創(chuàng)建了一個(gè)請(qǐng)求key,然后調(diào)用了shouldReadFromMemoryCache方法,這里如果我們?cè)O(shè)置了可以存儲(chǔ)到內(nèi)存緩存,那么是會(huì)先去從內(nèi)存緩存讀取的,所以看看下面的Picasso的quickMemoryCacheCheck方法:
獲取內(nèi)存緩存
Bitmap quickMemoryCacheCheck(String key) {
Bitmap cached = cache.get(key);
if (cached != null) {
stats.dispatchCacheHit();
} else {
stats.dispatchCacheMiss();
}
return cached;
}
void dispatchCacheHit() {
handler.sendEmptyMessage(CACHE_HIT);
}
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case CACHE_HIT:
stats.performCacheHit();
break;
case CACHE_MISS:
stats.performCacheMiss();
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);
}
});
}
}
void performCacheHit() {
cacheHits++;
}
void performCacheMiss() {
cacheMisses++;
}
這里很簡(jiǎn)單,直接從Picasso的cache中獲取緩存的bitmap,如果沒有則調(diào)用stats.dispatchCacheHit()方法,stats其實(shí)就是對(duì)各種結(jié)果和回調(diào)做一個(gè)通知的作用。也可以看一下,最終調(diào)用到performCacheHit,對(duì)成功獲取的緩存數(shù)++;那么dispatchCacheMiss應(yīng)該就是沒拿到緩存的計(jì)數(shù)++了。
回到上面的quickMemoryCacheCheck方法返回的bitmap,,如果bitmap 不為空表示獲取緩存成功,沒必要繼續(xù)請(qǐng)求了,因此調(diào)用cancelRequest,然后調(diào)用setBitmap方法:
static void setBitmap(ImageView target, Context context, Bitmap bitmap,
Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
Drawable placeholder = target.getDrawable();
if (placeholder instanceof Animatable) {
((Animatable) placeholder).stop();
}
PicassoDrawable drawable =
new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
target.setImageDrawable(drawable);
}
直接看重點(diǎn)的一行,target.setImageDrawable(drawable),到這里就是將bitmap設(shè)置到imageview了,PicassoDrawable 其實(shí)是對(duì)bitmap進(jìn)行的封裝。再回到獲取內(nèi)存緩存并setbitmap的位置,這里我們第一次加載應(yīng)該是沒有緩存的,所以我們繼續(xù)看下去,沒有緩存的情況。
setPlaceholder就是設(shè)置占位符,這個(gè)不是重點(diǎn),看下面的new ImageViewAction方法,傳入了picasso, target, request, memoryPolicy, networkPolicy, errorResId,errorDrawable, requestKey, tag, callback, noFade一系列參數(shù),生成了一個(gè)ImageViewAction,那么我們重點(diǎn)看這個(gè)action是干嘛的:
class ImageViewAction extends Action<ImageView> {
Callback callback;
ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
Callback callback, boolean noFade) {
super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
tag, noFade);
this.callback = callback;
}
@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;
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
if (callback != null) {
callback.onSuccess();
}
}
@Override public void error(Exception e) {
ImageView target = this.target.get();
if (target == null) {
return;
}
Drawable placeholder = target.getDrawable();
if (placeholder instanceof Animatable) {
((Animatable) placeholder).stop();
}
if (errorResId != 0) {
target.setImageResource(errorResId);
} else if (errorDrawable != null) {
target.setImageDrawable(errorDrawable);
}
if (callback != null) {
callback.onError(e);
}
}
ImageViewAction 主要實(shí)現(xiàn)的方法就是complete和error方法,這兩個(gè)的調(diào)用還是父類action中的,主要是對(duì)請(qǐng)求完成后的回調(diào)做處理,設(shè)置成功的圖片或者設(shè)置加載出錯(cuò)的圖片。所以主要的功能還是再父類Action中?;氐絀mageViewAction創(chuàng)建完成后,into方法的最后一行,調(diào)用的是picasso.enqueueAndSubmit(action)執(zhí)行這個(gè)action,我們跟進(jìn)去看一下:
執(zhí)行請(qǐng)求
void enqueueAndSubmit(Action action) {
Object target = action.getTarget();
if (target != null && targetToAction.get(target) != action) {
cancelExistingRequest(target);
targetToAction.put(target, action);
}
submit(action);
}
void submit(Action action) {
dispatcher.dispatchSubmit(action);
}
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
}
void performSubmit(Action action) {
performSubmit(action, true);
}
void performSubmit(Action action, boolean dismissFailed) {
if (pausedTags.contains(action.getTag())) {
pausedActions.put(action.getTarget(), action);
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
"because tag '" + action.getTag() + "' is paused");
}
return;
}
BitmapHunter hunter = hunterMap.get(action.getKey());
if (hunter != null) {
hunter.attach(action);
return;
}
if (service.isShutdown()) {
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
}
return;
}
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
hunter.future = service.submit(hunter);
hunterMap.put(action.getKey(), hunter);
if (dismissFailed) {
failedActions.remove(action.getTarget());
}
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
}
}
這里的if判斷,就是判斷如果之前存儲(chǔ)過(guò)跟這次不一樣的加載請(qǐng)求,那么取消掉上一次的加載,存儲(chǔ)當(dāng)前這一次的加載請(qǐng)求,然后調(diào)用submit方法,我們看submit方法通過(guò)dispatcher下發(fā)了提交請(qǐng)求任務(wù)的消息,最終調(diào)用到了performSubmit方法。這里我們看第一個(gè)if判斷
在了解判斷之前,來(lái)看看這個(gè)pausedTags是什么,怎么賦值的:
public class SampleScrollListener implements AbsListView.OnScrollListener {
private final Context context;
public SampleScrollListener(Context context) {
this.context = context;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
final Picasso picasso = Picasso.get();
if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) {
picasso.resumeTag(context);
} else {
picasso.pauseTag(context);
}
}
}
最終發(fā)現(xiàn),pausedTags添加的地方,就是滾動(dòng)監(jiān)聽,當(dāng)屏幕暫停時(shí),就將當(dāng)前的context存儲(chǔ)到暫停的tag中,否則就從暫停的pausedTags中移除。那么回到上面的performSubmit方法我們就明白了,意思就是只有當(dāng)屏幕沒有滑動(dòng)的時(shí)候,才允許去加載當(dāng)前的請(qǐng)求,那么繼續(xù)往下看,hunterMap.get方法獲取到了BitmapHunter,這里的action.getKey()就是前面我們獲取內(nèi)存緩存的key,那么bitmapHunter是什么呢,他說(shuō)一個(gè)實(shí)現(xiàn)了Runnable接口的類,所有的耗時(shí)操作包括請(qǐng)求圖片,解碼圖片,圖片轉(zhuǎn)換等都在bitmapHunter中執(zhí)行。這里獲取BitmapHunter 后去判斷是否為空,如果不為空表示當(dāng)前正在執(zhí)行請(qǐng)求,那么就不會(huì)再去執(zhí)行直接return,否則我們看下面的forRequest方法:
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
Action action) {
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
if (requestHandler.canHandleRequest(request)) {
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
}
這里沒有BitmapHunter ,那么就去創(chuàng)建一個(gè)BitmapHunter 并且返回,通過(guò)找到對(duì)應(yīng)的requestHandlers 去生成對(duì)應(yīng)的BitmapHunter,這里我們地方requestHandler應(yīng)該就是請(qǐng)求相關(guān)的NetworkRequestHandler,我們回到返回值的地方,下面調(diào)用service.submit(hunter)表示去執(zhí)行請(qǐng)求,請(qǐng)求的結(jié)果存儲(chǔ)在hunter.future中,完成后緩存到hunterMap中,我們跟進(jìn)去看一下:
@Override
public Future<?> submit(Runnable task) {
PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
execute(ftask);
return ftask;
}
@Override public void run() {
try {
updateThreadName(data);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
}
result = hunt();
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
} catch (NetworkRequestHandler.ResponseException e) {
if (!NetworkPolicy.isOfflineOnly(e.networkPolicy) || e.code != 504) {
exception = e;
}
dispatcher.dispatchFailed(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);
}
}
這里直接執(zhí)行execute方法,那么我們就看BitmapHunter的run方法了,直接看result = hunt();這一行,可以看到通過(guò)hunt方法拿到請(qǐng)求的返回值了,result是一個(gè)Bitmap類型,那么我們就重點(diǎn)看hunt方法了。
hunt()
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
}
return bitmap;
}
}
networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
loadedFrom = result.getLoadedFrom();
exifOrientation = result.getExifOrientation();
bitmap = result.getBitmap();
// If there was no Bitmap then we need to decode it from the stream.
if (bitmap == null) {
Source source = result.getSource();
try {
bitmap = decodeStream(source, data);
} finally {
try {
//noinspection ConstantConditions If bitmap is null then source is guranteed non-null.
source.close();
} catch (IOException ignored) {
}
}
}
}
if (bitmap != null) {
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId());
}
stats.dispatchBitmapDecoded(bitmap);
if (data.needsTransformation() || exifOrientation != 0) {
synchronized (DECODE_LOCK) {
if (data.needsMatrixTransform() || exifOrientation != 0) {
bitmap = transformResult(data, bitmap, exifOrientation);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
}
}
if (data.hasCustomTransformations()) {
bitmap = applyCustomTransformations(data.transformations, bitmap);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
}
}
}
if (bitmap != null) {
stats.dispatchBitmapTransformed(bitmap);
}
}
}
return bitmap;
}
這里我們又調(diào)了一次shouldReadFromMemoryCache,這里再次獲取內(nèi)存緩存也沒有沖突,防止前面請(qǐng)求的時(shí)候獲取緩存不及時(shí),這里我們直接看requestHandler.load返回RequestHandler.Result的方法,跟進(jìn)去看一下:
@Override public Result load(Request request, int networkPolicy) throws IOException {
okhttp3.Request downloaderRequest = createRequest(request, networkPolicy);
Response response = downloader.load(downloaderRequest);
ResponseBody body = response.body();
if (!response.isSuccessful()) {
body.close();
throw new ResponseException(response.code(), request.networkPolicy);
}
Picasso.LoadedFrom loadedFrom = response.cacheResponse() == null ? NETWORK : DISK;
if (loadedFrom == DISK && body.contentLength() == 0) {
body.close();
throw new ContentLengthException("Received response with 0 content-length header.");
}
if (loadedFrom == NETWORK && body.contentLength() > 0) {
stats.dispatchDownloadFinished(body.contentLength());
}
return new Result(body.source(), loadedFrom);
}
@NonNull @Override public Response load(@NonNull Request request) throws IOException {
return client.newCall(request).execute();
}
到這里就是直接通過(guò)okhttp3請(qǐng)求去獲取結(jié)果了,可能你奇怪為什么看到這里一直都是內(nèi)存緩存,沒有看到過(guò)磁盤緩存,因?yàn)榇疟P緩存是在okhttp3請(qǐng)求框架里面自己去做了,所以這個(gè)代碼里面是沒有的。看downloader.load方法內(nèi)部其實(shí)還是使用的OK HTTP的newCall創(chuàng)建請(qǐng)求并執(zhí)行的,那么回調(diào)結(jié)果,返回上一層通過(guò)result.getBitmap()獲取到bitmap對(duì)象,如果為空,那么就再去解碼拿到Source并轉(zhuǎn)換成bitmap。后面就是回調(diào)了,返回到BitmapHunter的Run方法,拿到bitmap后調(diào)用dispatcher.dispatchComplete(this)方法:
拿到bitmap,回調(diào)結(jié)果
void performComplete(BitmapHunter hunter) {
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
cache.set(hunter.getKey(), hunter.getResult());
}
hunterMap.remove(hunter.getKey());
batch(hunter);
if (hunter.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
}
}
在拿到hunter后,立馬通過(guò)判斷是否需要內(nèi)存緩存來(lái)決定是否將結(jié)果緩存到內(nèi)存緩存,而下面的hunterMap也在請(qǐng)求成功后移除對(duì)應(yīng)的請(qǐng)求緩存,然后調(diào)用batch方法:
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
if (hunter.result != null) {
hunter.result.prepareToDraw();
}
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
拿到hunter.result后調(diào)用bitmap的prepareToDraw方法準(zhǔn)備填充到image view,然后存儲(chǔ)到batch中。方便后面對(duì)bitmapHunter同意管理:
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;
}
void complete(BitmapHunter hunter) {
Action single = hunter.getAction();
List<Action> joined = hunter.getActions();
boolean hasMultiple = joined != null && !joined.isEmpty();
boolean shouldDeliver = single != null || hasMultiple;
if (!shouldDeliver) {
return;
}
Uri uri = hunter.getData().uri;
Exception exception = hunter.getException();
Bitmap result = hunter.getResult();
LoadedFrom from = hunter.getLoadedFrom();
if (single != null) {
deliverAction(result, from, single, exception);
}
if (hasMultiple) {
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = joined.size(); i < n; i++) {
Action join = joined.get(i);
deliverAction(result, from, join, exception);
}
}
if (listener != null && exception != null) {
listener.onImageLoadFailed(this, uri, exception);
}
}
第一行的single也就是表示是否只有這一個(gè),如果是則獲取到bitmap后直接回調(diào),否則就遍歷每一個(gè)Action都調(diào)用deliverAction回調(diào),我們繼續(xù)看deliverAction方法:
private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
if (action.isCancelled()) {
return;
}
if (!action.willReplay()) {
targetToAction.remove(action.getTarget());
}
if (result != null) {
if (from == null) {
throw new AssertionError("LoadedFrom cannot be null.");
}
action.complete(result, from);
if (loggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);
}
} else {
action.error(e);
if (loggingEnabled) {
log(OWNER_MAIN, VERB_ERRORED, action.request.logId(), e.getMessage());
}
}
}
重點(diǎn)還是關(guān)注在action.complete(result, from)方法:
@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;
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
if (callback != null) {
callback.onSuccess();
}
}
static void setBitmap(ImageView target, Context context, Bitmap bitmap,
Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
Drawable placeholder = target.getDrawable();
if (placeholder instanceof Animatable) {
((Animatable) placeholder).stop();
}
PicassoDrawable drawable =
new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
target.setImageDrawable(drawable);
}
最終調(diào)用action.complete實(shí)際上還是調(diào)用的image viewAction,因?yàn)橐婚_始的into方法,我們創(chuàng)建的action就是image viewAction,因此在complete中可以看到target.get()獲取我們傳進(jìn)來(lái)的imageview,然后調(diào)用PicassoDrawable的setBitmap方法設(shè)置圖片到view,最終調(diào)用callback.onSuccess()回調(diào)到我們使用Picasso加載圖片到地方,前提是我們?cè)O(shè)置了這個(gè)callback。
總結(jié)
Picasso通過(guò)get方法創(chuàng)建單例實(shí)例,然后通過(guò)build方法創(chuàng)建請(qǐng)求下載器、內(nèi)存緩存、線程池和Dispatcher ,并最終生成Picasso對(duì)象。磁盤緩存和內(nèi)存緩存都是根據(jù)手機(jī)容量和內(nèi)存來(lái)判斷的,內(nèi)存為app可用內(nèi)存的七分之一。Dispatcher則在全程中起到的一個(gè)調(diào)度的作用,回調(diào)和傳遞各種信息,內(nèi)部有一個(gè)網(wǎng)絡(luò)狀態(tài)的廣播監(jiān)聽,根據(jù)手機(jī)網(wǎng)絡(luò)的情況2G-4G來(lái)設(shè)置線程池不同的核心線程數(shù),然后還可以監(jiān)聽飛行模式。load方法則是通過(guò)RequestCreator 創(chuàng)建Request的build對(duì)象,最終調(diào)用into方法通過(guò)build對(duì)象創(chuàng)建Request對(duì)象并生成一個(gè)緩存key,通過(guò)緩存key在內(nèi)存緩存中查到是否存在緩存,不存在則創(chuàng)建一個(gè)Action對(duì)象,通過(guò)action對(duì)象找到對(duì)應(yīng)NetworkRequestHandler 并生成BitmapHunter,BitmapHunter本身是一個(gè)線程,最終調(diào)用PicassoExecutorService線程池的submit方法執(zhí)行BitmapHunter,最終調(diào)用requestHandler的load方法通過(guò)OK HTTP請(qǐng)求做請(qǐng)求和磁盤緩存的操作獲得Response ,然后解碼拿到bitmap回調(diào)給dispatcher,然后緩存到內(nèi)存,并且回調(diào)到image viewAction的complete方法,最終通過(guò)PicassoDrawable的setBitmap方法設(shè)置圖片資源到image view中。
到這里Picasso的源碼分析就結(jié)束了,在看過(guò)了Glide源碼之后,再看Picasso可能突然覺得輕松了許多,畢竟Glide的功能還是很豐富的,類有比較復(fù)雜,所以閱讀起來(lái)相對(duì)比較麻煩。那么對(duì)比我們之前分析的Glide源碼,我們看看他們的區(qū)別在哪:
- 加載數(shù)據(jù)類型的區(qū)別
glide支持gif圖加載,Picasso不支持。 - 緩存的區(qū)別
glide內(nèi)存緩存和磁盤緩存都做了二級(jí)緩存,Picasso只有一步內(nèi)存緩存,磁盤緩存還是OkHttp做的。 - Picasso對(duì)飛行模式做了單獨(dú)的監(jiān)聽,并對(duì)網(wǎng)絡(luò)狀態(tài)的234G做了監(jiān)聽,根據(jù)不同的網(wǎng)絡(luò)設(shè)置不同的線程池核心數(shù)
- Picasso對(duì)在滑動(dòng)狀態(tài)的view不會(huì)去請(qǐng)求,只有當(dāng)停止滑動(dòng)的時(shí)候才會(huì)請(qǐng)求
- Glide綁定了activity的生命周期,當(dāng)activity銷毀則取消對(duì)應(yīng)的請(qǐng)求和回收資源避免內(nèi)存泄漏。
- Glide的默認(rèn)圖片格式是RGB_565,Picasso則是ARGB_8888,所以Picasso加載的圖片緩存是Glide的2倍,但是清晰度比Glide高
- Picasso會(huì)默認(rèn)加載url對(duì)應(yīng)的全尺寸圖片,Glide則會(huì)根據(jù)imageview的大小緩存對(duì)應(yīng)尺寸的圖片,所以在內(nèi)存方面,Glide優(yōu)于Picasso