Picasso-原始碼解析(二)
前言
前面一篇文章簡單的介紹了Picasso的使用,已經整個原始碼呼叫的流程,過了一遍。但是其中還有很多的細節我們並沒有去涉及到。今天在昨天的基礎之上再進行深入。
Picasso物件
前面一篇文章我們是從 Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(ivTest)
慢慢深入的,這裡我們也要找個可以深入的物件,就是最重要的 Picasso
物件。
因為我們在使用的時候就是直接 Picasso.get().***
這樣去呼叫的,所以說我們先了解下 Picasso
所有的api,對整個框架的理解,以及對 Picasso
更有效率的使用。
public方法

image.png
我們的as很強大,直接左側欄檢視當前類的公用方法.
這裡就把方法一個個介紹下。便於大家理解。
- areIndicatorsEnabled(),setIndicatorsEnabled()
@SuppressWarnings("UnusedDeclaration") public void setIndicatorsEnabled(boolean enabled) { indicatorsEnabled = enabled; } @SuppressWarnings("UnusedDeclaration") public boolean areIndicatorsEnabled() { return indicatorsEnabled; }
用來設定和獲取是否顯示當前載入圖片的方式,上一篇文章有使用過,就是顯示當前圖片是由三級快取中的哪一個載入的。
-
cancelRequest
看名字就知道,是直接用來取消請求的。
這裡可以深入下。
public void cancelRequest(@NonNull ImageView view) { if (view == null) { throw new IllegalArgumentException("view cannot be null."); } cancelExistingRequest(view); } void cancelExistingRequest(Object target) { checkMain(); //前面在載入圖片的時候是會target-action以key-value的方式儲存在targetToAction中,後面我會指出,在哪裡儲存的。這裡直接通過target獲取到action,然後cancel Action action = targetToAction.remove(target); if (action != null) { action.cancel(); dispatcher.dispatchCancel(action); } //這裡就比較有意思,因為第一篇文章我們介紹過,如果是`ImageView`的話,很可能會使用fit方法去適配`ImageView`的寬高,就有使用到DeferredRequestCreator,所以這裡要移除 if (target instanceof ImageView) { ImageView targetImageView = (ImageView) target; DeferredRequestCreator deferredRequestCreator = targetToDeferredRequestCreator.remove(targetImageView); if (deferredRequestCreator != null) { deferredRequestCreator.cancel(); } } }
-
cancelTag,pauseTag,resumeTag
這3個一起說,這3個可以說是批量操作符。可以簡單的看一下。
Picasso.get().load("http://i.imgur.com/DvpvklR.png") .tag("test") .into(ivTest) Picasso.get() .load("http://i.imgur.com/DvpvklR.png") .tag("test") .into(ivTest2) Picasso.get().cancelTag("test")
可以先tag標記下每個請求,然後可以批量處理。
分別是取消,暫停,恢復。
- setLoggingEnabled,isLoggingEnabled
public void setLoggingEnabled(boolean enabled) { loggingEnabled = enabled; } public boolean isLoggingEnabled() { return loggingEnabled; }
是否啟動日誌
- getSnapshot
@SuppressWarnings("UnusedDeclaration") public StatsSnapshot getSnapshot() { return stats.createSnapshot(); }
獲取到 Picasso
的一個快照。
Picasso.get().snapshot.dump()
然後直接列印。
===============BEGIN PICASSO STATS =============== Memory Cache Stats Max Cache Size: 57521883 Cache Size: 0 Cache % Full: 0 Cache Hits: 0 Cache Misses: 0 Network Stats Download Count: 0 Total Download Size: 0 Average Download Size: 0 Bitmap Stats Total Bitmaps Decoded: 0 Total Bitmap Size: 0 Total Transformed Bitmaps: 0 Total Transformed Bitmap Size: 0 Average Bitmap Size: 0 Average Transformed Bitmap Size: 0 ===============END PICASSO STATS ===============
這個就灰常的方便了,直接看當前Picasso的使用狀態
-
invalidate
因為
Picasso
使用了LruCache
快取到記憶體中,key-value與url-bitmap對應起來(實際上不是儲存Bitmap,而是自己封裝了另外一個類,但是裡面是有Bitmap的,這裡先不具體)。invalidate可以移除快取。
public void invalidate(@Nullable Uri uri) { if (uri != null) { cache.clearKeyUri(uri.toString()); } }
-
load
這個就不說了,上一篇文章介紹了很多了。
重要物件介紹
上面簡單的了Picasso物件,但是還有很多重要的物件。
我們可以從 Picasso.Builder
中去尋找,因為這裡面都是我們可以配置的物件。
public static class Builder { private final Context context; private Downloader downloader; private ExecutorService service; private Cache cache; private Listener listener; private RequestTransformer transformer; private List<RequestHandler> requestHandlers; private Bitmap.Config defaultBitmapConfig; private boolean indicatorsEnabled; private boolean loggingEnabled; ...
這麼多可以配置的物件,ndicatorsEnabled,loggingEnabled,context先不說了。
我們來先來簡單介紹下另外幾個。
-
Downloader downloader
預設實現OkHttp3Downloader
@NonNull @Override public Response load(@NonNull Request request) throws IOException { return client.newCall(request).execute();
}
```
其實是可以自己複寫,使用自己的http框架
-
ExecutorService service
預設實現PicassoExecutorService
//建立了一個執行緒池,預設都是3個核心執行緒,然後還有一個有優先順序的阻塞佇列 PicassoExecutorService() { super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
}
```
這裡其實還有一個比較有意思的方法
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); } }
就是會根據網路的情況使用不同的執行緒數。可以學習下。
也可以自定義
-
Cache cache
預設使用
LruCache
public LruCache(@NonNull Context context) { this(Utils.calculateMemoryCacheSize(context)); } ... 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); }
這裡也很有意思,根據當前的app所能使用的記憶體值獲取一個比較合適的快取記憶體的最大值。自己可以複寫調整。
-
Listener listener
沒有預設值
public interface Listener { void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception); }
很顯然是用來全域性監聽圖片載入失敗的事件。
-
RequestTransformer transformer
預設
RequestTransformer IDENTITY = new RequestTransformer() { @Override public Request transformRequest(Request request) { return request; } };
其實就是一個request的全域性的一個轉化。預設不轉化。
- List<RequestHandler> requestHandlers
前面已經介紹過了預設有很多種requestHandlers,也可以自己定義 - Bitmap.Config defaultBitmapConfig
預設為null
在requestHandler裡面的createBitmapOptions有使用到
static BitmapFactory.Options createBitmapOptions(Request data) { final boolean justBounds = data.hasSize(); final boolean hasConfig = data.config != null; BitmapFactory.Options options = null; if (justBounds || hasConfig || data.purgeable) { options = new BitmapFactory.Options(); options.inJustDecodeBounds = justBounds; options.inInputShareable = data.purgeable; options.inPurgeable = data.purgeable; //在這裡,如果有config,就配置下,如果沒有那麼就預設 if (hasConfig) { options.inPreferredConfig = data.config; } } return options; }

image.png
一共這麼多選項,可以選擇。
總結
Picasso用起來也挺爽的,後面有空學習下Glide和Fresco做下對比