picasso使用簡單,如下
picasso.with(context).load("http://i.imgur.com/dvpvklr.png").into(imageview);
主要有以下一些特性:
在adapter中回收和取消目前的下載下傳;
使用最少的記憶體完成複雜的圖形轉換操作;
自動的記憶體和硬碟緩存;
圖形轉換操作,如變換大小,旋轉等,提供了接口來讓使用者可以自定義轉換操作;
加載載網絡或本地資源;
lrucacha,主要是get和set方法,存儲的結構采用了linkedhashmap,這種map内部實作了lru算法(least recently used 近期最少使用算法)。
this.map = new linkedhashmap<string, bitmap>(0, 0.75f, true);
最後一個參數的解釋:
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.
因為可能會涉及多線程,是以在存取的時候都會加鎖。而且每次set操作後都會判斷目前緩存區是否已滿,如果滿了就清掉最少使用的圖形。代碼如下
private void trimtosize(int maxsize) {
while (true) {
string key;
bitmap value;
synchronized (this) {
if (size < 0 || (map.isempty() && size != 0)) {
throw new illegalstateexception(getclass().getname()
+ ".sizeof() is reporting inconsistent results!");
}
if (size <= maxsize || map.isempty()) {
break;
map.entry<string, bitmap> toevict = map.entryset().iterator()
.next();
key = toevict.getkey();
value = toevict.getvalue();
map.remove(key);
size -= utils.getbitmapbytes(value);
evictioncount++;
}
}
}
所有對圖形的操作都會記錄在這裡,供之後圖形的建立使用,如重新計算大小,旋轉角度,也可以自定義變換,隻需要實作transformation,一個bitmap轉換的接口。
public interface transformation {
/**
* transform the source bitmap into a new bitmap. if you create a new bitmap instance, you must
* call {@link android.graphics.bitmap#recycle()} on {@code source}. you may return the original
* if no transformation is required.
*/
bitmap transform(bitmap source);
* returns a unique key for the transformation, used for caching purposes. if the transformation
* has parameters (e.g. size, scale factor, etc) then these should be part of the key.
string key();
當操作封裝好以後,會将request傳到另一個結構中action。
action代表了一個具體的加載任務,主要用于圖檔加載後的結果回調,有兩個抽象方法,complete和error,也就是當圖檔解析為bitmap後使用者希望做什麼。最簡單的就是将bitmap設定給imageview,失敗了就将錯誤通過回調通知到上層。
imageviewaction實作了action,在complete中将bitmap和imageview組成了一個picassodrawable,裡面會實作淡出的動畫效果。
@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 debugging = picasso.debugging;
picassodrawable.setbitmap(target, context, result, from, nofade,
debugging);
if (callback != null) {
callback.onsuccess();
}
有了加載任務,具體的圖檔下載下傳與解析是在哪裡呢?這些都是耗時的操作,應該放在異步線程中進行,就是下面的bitmaphunter。
bitmaphunter是一個runnable,其中有一個decode的抽象方法,用于子類實作不同類型資源的解析。
public void run() {
try {
thread.currentthread()
.setname(utils.thread_prefix + data.getname());
result = hunt();
if (result == null) {
dispatcher.dispatchfailed(this);
} else {
dispatcher.dispatchcomplete(this);
} catch (ioexception e) {
exception = e;
dispatcher.dispatchretry(this);
} catch (exception e) {
dispatcher.dispatchfailed(this);
} finally {
thread.currentthread().setname(utils.thread_idle_name);
abstract bitmap decode(request data) throws ioexception;
bitmap hunt() throws ioexception {
bitmap bitmap;
if (!skipmemorycache) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchcachehit();
loadedfrom = memory;
return bitmap;
bitmap = decode(data);
if (bitmap != null) {
stats.dispatchbitmapdecoded(bitmap);
if (data.needstransformation() || exifrotation != 0) {
synchronized (decode_lock) {
if (data.needsmatrixtransform() || exifrotation != 0) {
bitmap = transformresult(data, bitmap, exifrotation);
}
if (data.hascustomtransformations()) {
bitmap = applycustomtransformations(
data.transformations, bitmap);
stats.dispatchbitmaptransformed(bitmap);
return bitmap;
可以看到,在decode生成原始bitmap,之後會做需要的轉換transformresult和applycustomtransformations。最後在将最終的結果傳遞到上層dispatcher.dispatchcomplete(this)。
基本的組成元素有了,那這一切是怎麼連接配接起來運作呢,答案是dispatcher。
在bitmaphunter成功得到bitmap後,就是通過dispatcher将結果傳遞出去的,當然讓bitmaphunter執行也要通過dispatcher。
dispatcher内有一個handlerthread,所有的請求都會通過這個thread轉換,也就是請求也是異步的,這樣應該是為了ui線程更加流暢,同時保證請求的順序,因為handler的消息隊列。
外部調用的是dispatchxxx方法,然後通過handler将請求轉換到對應的performxxx方法。
例如生成action以後就會調用dispather的dispatchsubmit()來請求執行,
void dispatchsubmit(action action) {
handler.sendmessage(handler.obtainmessage(request_submit, action));
handler接到消息後轉換到performsubmit方法
void performsubmit(action action) {
bitmaphunter hunter = huntermap.get(action.getkey());
if (hunter != null) {
hunter.attach(action);
if (service.isshutdown()) {
hunter = forrequest(context, action.getpicasso(), this, cache, stats,
action, downloader);
hunter.future = service.submit(hunter);
huntermap.put(action.getkey(), hunter);
這裡将通過action得到具體的bitmaphunder,然後交給executorservice執行。
下面是picasso.with(context).load("http://i.imgur.com/dvpvklr.png").into(imageview)的過程,
public static picasso with(context context) {
if (singleton == null) {
singleton = new builder(context).build();
return singleton;
public picasso build() {
context context = this.context;
if (downloader == null) {
downloader = utils.createdefaultdownloader(context);
if (cache == null) {
cache = new lrucache(context);
if (service == null) {
service = new picassoexecutorservice();
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, stats, debugging);
在picasso.with()的時候會将執行所需的所有必備元素建立出來,如緩存cache、執行executorservice、排程dispatch等,在load()時建立request,在into()中建立action、bitmaphunter,并最終交給dispatcher執行。