Picasso源碼分析

首先從調用第一行代碼看起

// Trigger the download of the URL asynchronously into the image view.

Picasso.with()

.load(url)

.placeholder(R.drawable.placeholder)

.error(R.drawable.error)

.resizeDimen(R.dimen.list_detail_image_size,R.dimen.list_detail_image_size)

.centerInside()

.tag(context)

.into(holder.image);

很顯然是單例模式

public staticPicassowith() {

    if(singleton==null) {

         synchronized(Picasso.class) {

          if(singleton==null) {

             if(PicassoProvider.context==null) {

                    throw newIllegalStateException("context == null");

             }

             singleton=newBuilder(PicassoProvider.context).build();

          }

      }

  }

returnsingleton;

}

裏面還有個PicassoProvider,在這個類裏面直接獲取的Context

@RestrictTo(LIBRARY)

public final class  PicassoProvider  extends  ContentProvider {

   static Context  context;

@Override public booleanonCreate() {

    context= getContext();

     return true;

}

再看通過內部Builder的build方法創建一些默認的內部依賴的類.

/** Create the {@linkPicasso} instance. */

public Picasso build() {

      Context context =this.context;

      if(downloader==null) {

           downloader=new OkHttp3Downloader(context);//下載器

       }

       if(cache==null) {

         cache=new LruCache(context);//緩存

        }

          if(service==null) {

              service=new PicassoExecutorService();//線程池

           }

          if(transformer==null) {

                    transformer= RequestTransformer.IDENTITY;//Request轉換

           }

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);

}

再看

public RequestCreator load(@NullableString 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));

}

將String轉換爲Uri,最終調用。生成一個RequestCreator對象。

public  RequestCreator load(@NullableUri uri) {

         return new RequestCreator(this,uri,0);

}

這個對象存儲了請求的基本信息,如果是請求網絡則第三個參數爲0如果是資源文件則第三個參數就是資源id

public class RequestCreator {

   private static final AtomicInteger nextId=newAtomicInteger();

    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;

     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);

}

再往下看。設置還未下載圖片之前佔位資源id。

public RequestCreator placeholder(@DrawableRes int placeholderResId) {

       if(!setPlaceholder) {//默認這個值爲true,如果調用noPlaceholder方法則爲false

               throw new IllegalStateException("Already explicitly declared as no placeholder.");

         }

        if(placeholderResId ==0) {

              throw new IllegalArgumentException("Placeholder image resource invalid.");

          }

          if(placeholderDrawable!=null) {

             throw new IllegalStateException("Placeholder image already set.");

          }

              this.placeholderResId= placeholderResId;

         return this;

    }

同樣的設置錯誤是顯示資源Id

/** An error drawable to be used if the request image could not be loaded. */

public RequestCreator error(@DrawableRes int  errorResId) {

if(errorResId ==0) {

throw new  IllegalArgumentException("Error image resource invalid.");

}

if(errorDrawable!=null) {

throw new  IllegalStateException("Error image already set.");

}

this.errorResId= errorResId;

return this;

}

fitz表示要處理下載後的圖片要適應圖片控件的大小

/*** Attempt to resize the image to fit exactly into the target {@linkImageView}'s bounds. This* will result in delayed execution of the request until the {@linkImageView} has been laid out.*

*Note:This method works only when your target is an {@linkImageView}.*/

public   RequestCreator  fit() {

        deferred=true;

         return this;

}

Tag表示一個類關聯當前請求,下面有個警告,說只要tag狀態是暫停或者有活動的請求,picasso一直會保持tag的引用,有可能有內存泄露

/*** Assign a tag to this request. Tags are an easy way to logically associate* related requests that can be managed together e.g. paused, resumed,* or canceled.*

* You can either use simple {@linkString} tags or objects that naturally* define the scope of your requests within your app such as a* {@linkandroid.content.Context}, an {@linkandroid.app.Activity}, or a* {@linkandroid.app.Fragment}.**WARNING:: Picasso will keep a reference to the tag for* as long as this tag is paused and/or has active requests. Look out for* potential leaks.**@seePicasso#cancelTag(Object)*@seePicasso#pauseTag(Object)*@seePicasso#resumeTag(Object)*/publicRequestCreatortag(@NonNullObject tag) {

if(tag ==null) {

throw new  IllegalArgumentException("Tag invalid.");

}

if(this.tag!=null) {

throw new   IllegalStateException("Tag already set.");

}

this.tag= tag;

return this;

}

然後查看into方法,註釋裏面說對target保持弱引用

/*** Asynchronously fulfills the request into the specified {@linkImageView}.*

*Note:This method keeps a weak reference to the {@linkImageView} instance and will* automatically support object recycling.*/

public void  into(ImageView target) {

        into(target, null);

}

真正調用帶回調參數的方法。如果直接調用這個方法,註釋裏面提示也說明了,callback這個參數會是一個強引用,可能會導致阻止你的activity或者fragment被垃圾回收器回收.如果你調用這個方法,強烈建議你調用canlRequest阻止內存泄露.

/*** Asynchronously fulfills the request into the specified {@linkImageView} and invokes the* target {@linkCallback} if it's not {@codenull}.*

*Note:The {@linkCallback} param is a strong reference and will prevent your* {@linkandroid.app.Activity} or {@linkandroid.app.Fragment} from being garbage collected. If* you use this method, it isstronglyrecommended you invoke an adjacent* {@linkPicasso#cancelRequest(android.widget.ImageView)} call to prevent temporary leaking.*/

public void   into(ImageView target,Callback callback) {

longstarted = 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.");

}

intwidth = target.getWidth();

intheight = target.getHeight();

if(width ==0|| height ==0|| target.isLayoutRequested()) {

if(setPlaceholder) {

setPlaceholder(target,getPlaceholderDrawable());

}

picasso.defer(target, newDeferredRequestCreator(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() {

       returnuri!=null||resourceId!=0;

}

如果沒有下載地址,又沒有資源id,取消請求,需要設置佔位背景圖片則設置佔位背景圖片

if(!data.hasImage()) {

picasso.cancelRequest(target);

if(setPlaceholder) {

setPlaceholder(target,getPlaceholderDrawable());

}

return;

}

設置佔位圖片背景就比較簡單了。

private  Drawable    getPlaceholderDrawable() {

if(placeholderResId!=0) {

return     picasso.context.getResources().getDrawable(placeholderResId);

}else{

return       placeholderDrawable;// This may be null which is expected and desired behavior.

}

}

後面如果設置是要處理下載的圖片大小.則deferred爲true .如果設置了

public   RequestCreator      fit() {

deferred=true;

return this;

}

此處爲false。則直接跳過

if(deferred) {

if(data.hasSize()) {

throw new   IllegalStateException("Fit cannot be used with resize.");

}

intwidth = target.getWidth();

intheight = target.getHeight();

if(width ==0|| height ==0|| target.isLayoutRequested()) {

if(setPlaceholder) {

setPlaceholder(target,getPlaceholderDrawable());

}

picasso.defer(target, newDeferredRequestCreator(this,target,callback));

return;

}

data.resize(width,height);

}

生成一個request對象。根據request對象裏面的屬性又生成一個requestKey.首先判斷是否從內存中獲取,默認是有設置了內存緩存的,所以先從內存中獲取bitmap對象。如果有當前對象,則取消request請求.將bitmap設置到imageview上面去。

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;

}

}

如果緩存中沒有。先設置佔位背景。再生成一個action對象提交

if(setPlaceholder) {

setPlaceholder(target,getPlaceholderDrawable());

}

Action action =

new  ImageViewAction(picasso,target,request,memoryPolicy,networkPolicy,errorResId,

errorDrawable,requestKey,tag,callback,noFade);

picasso.enqueueAndSubmit(action);

首先判斷當前action是否已經在列隊中,如果已經存在,則先取消。再重新放進去。最後調用submit。這裏這個target對象就是imageview對象。一個imageview對象和一個action對象保存在targetToAction Map集合當中。這裏這麼處理,就巧妙的解決了listview上下滑動時imageview對象複用時不會造成加載圖片亂序.舉個栗子,如果當期imageview還在加載圖片,這時listivew滑動造成imageview的不可見時重新複用,然後再去加載新的圖片時就會把前面未加載的圖片請求取消。

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);

targetToAction.put(target,action);

}

submit(action);

}

提交action

void    submit(Action action) {

dispatcher.dispatchSubmit(action);

}

僅發送一個請求

void  dispatchSubmit(Action action) {

handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT,action));

}

handler裏面的處理

@Override

public void   handleMessage(finalMessage msg) {

switch(msg.what) {

caseREQUEST_SUBMIT: {

Action action = (Action) msg.obj;

dispatcher.performSubmit(action);

break;

}

又調用

void   performSubmit(Action action) {

performSubmit(action, true);

}

此方法比較重要,一一着重分析

void  performSubmit(Action action, booleandismissFailed) {

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;

}

//如果當前已經有url對應的action在下載了(url和key一一對應,action和imageview一一對應,一個bitmapHunter可能對應多個action)

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());

}

}

首先在pausedTagsSet集合中查看是否存在imageview。(看這集合的名字,應該是暫停當前請求的集合),如果存在,則又在pausedActions map集合裏面放入對應的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;

}

然後從hunterMap集合中根據action的key獲取一個BitmapHunter對象,BitmapHunter繼承Runable,可見應該最後從網絡獲取後生成bitmap處理的就是這個對象。

BitmapHunter hunter =hunterMap.get(action.getKey());

if(hunter !=null) {

hunter.attach(action);

return;

}

由於還沒有添加到hunterMap集合中,故這裏先拿到的是null。繼續往下走

判斷線程池是否關閉,如果關閉則直接返回。

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());

}

生成一個bitmapHunter。

staticBitmapHunterforRequest(Picasso picasso,Dispatcher dispatcher,Cache cache,Stats stats,

Action action) {

Request request = action.getRequest();

List requestHandlers = picasso.getRequestHandlers();

// Index-based loop to avoid allocating an iterator.

//noinspection ForLoopReplaceableByForEach

for(inti =0,count = requestHandlers.size();i < count;i++) {

RequestHandler requestHandler = requestHandlers.get(i);

if(requestHandler.canHandleRequest(request)) {

return newBitmapHunter(picasso,dispatcher,cache,stats,action,requestHandler);

}

}

return newBitmapHunter(picasso,dispatcher,cache,stats,action,ERRORING_HANDLER);

}

Picasso.getRequestHandlers()集合在picasso初始化的時候就添加了許多相應的RequestHandler。由名字可以看出如果是資源文件,則對象的是ResouceRequsetHandler處理,如果圖片在在SD卡,則會由FileRequestHandler處理,如果是URL,則由NetworkRequsetHandler處理。

allRequestHandlers.add(newResourceRequestHandler(context));

if(extraRequestHandlers !=null) {

allRequestHandlers.addAll(extraRequestHandlers);

}

allRequestHandlers.add(newContactsPhotoRequestHandler(context));

allRequestHandlers.add(newMediaStoreRequestHandler(context));

allRequestHandlers.add(newContentStreamRequestHandler(context));

allRequestHandlers.add(newAssetRequestHandler(context));

allRequestHandlers.add(newFileRequestHandler(context));

allRequestHandlers.add(newNetworkRequestHandler(dispatcher.downloader,stats));

requestHandlers= Collections.unmodifiableList(allRequestHandlers);

故這裏BitmapHunter傳入的就是NetworkRequestHandler對象。

然後把bitmapHunter提交到線程池.看PicassoExecutorSerice裏面的submit方法。PicassoFutureTask繼承了FutureTask,裏面包含了一個BitmapHunter

@Override

public   Future   submit(Runnable task) {

PicassoFutureTask ftask =new  PicassoFutureTask((BitmapHunter) task);

execute(ftask);

returnftask;

}

再查看BitmapHunter裏面的run方法

public void    run() {

try{

//根據Request更新當前線程名稱

updateThreadName(data);

if(picasso.loggingEnabled) {

log(OWNER_HUNTER,VERB_EXECUTING,getLogIdsForHunter(this));

}

result= hunt();

.....省略無關代碼

}

繼續hunt方法。依然判斷是否可以從緩存中獲取。默認是開啓的。首先依舊從緩存中獲取.如果有直接獲取。返回

Bitmaphunt()       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;

}

}

。。。。後面代碼暫時省略

繼續後面的分析

Bitmaphunt()  throws    IOException {

Bitmap bitmap =null;

....省略前面分析過的代碼

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) {

}

}

}

}

.....省略後面待分析的代碼

}

returnbitmap;

}

前面說了由於傳入的是個URL。所以requestHandler就是一個NetworkRequsetHandler對象。查看NetworkRequstHandler裏面的load方法,裏面的邏輯應該就是從網上下載圖片並且封裝成Result對象返回。

移步NetworkRequestHandler裏面的load方法.downloader就是picasso初始化的時候初始化的OkHttp3Downloader對象。OkHttp3Downloader裏面用到了OkHttp裏面的請求服務器的方法。具體不懂OkHttp的可以自行百度。獲取數據成功後封裝成Request返回

@Override

public    Result    load(Request request, intnetworkPolicy)throwsIOException {

okhttp3.Request downloaderRequest =createRequest(request,networkPolicy);

Response response =downloader.load(downloaderRequest);

ResponseBody body = response.body();

if(!response.isSuccessful()) {

body.close();

throw newResponseException(response.code(),request.networkPolicy);

}

// Cache response is only null when the response comes fully from the network. Both completely

// cached and conditionally cached responses will have a non-null cache response.

Picasso.LoadedFrom loadedFrom = response.cacheResponse() ==null?NETWORK:DISK;

// Sometimes response content length is zero when requests are being replayed. Haven't found

// root cause to this but retrying the request seems safe to do so.

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);

}

然後調用decodeStream方法,把獲取的數據轉換爲bitmap對象,這裏

static     Bitmap     decodeStream(Source source,Request request)throwsIOException {

BufferedSource bufferedSource = Okio.buffer(source);

....省略代碼

// We decode from a byte array because, a) when decoding a WebP network stream, BitmapFactory

// throws a JNI Exception, so we workaround by decoding a byte array, or b) user requested

// purgeable, which only affects bitmaps decoded from byte arrays.

if(isWebPFile || isPurgeable) {

byte[] bytes = bufferedSource.readByteArray();

...省略一些是否計算options參數的代碼

//通過bytes獲取bitmap對象

returnBitmapFactory.decodeByteArray(bytes,0,bytes.length,options);

}else{

InputStream stream = bufferedSource.inputStream();

...省略一些是否計算options參數的代碼

//通過輸入流獲取bitmap對象

Bitmap bitmap = BitmapFactory.decodeStream(stream, null,options);

if(bitmap ==null) {

// (發現bitmap爲null,我們將最終重試)

//Treat null as an IO exception, we will eventually retry.

throw newIOException("Failed to decode stream.");

}

returnbitmap;

}

}

繼續看hunt方法.

Bitmaphunt()    throws        IOException {

Bitmap bitmap =null;

....省略前面分析過的代碼

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;

}

判斷如果需要作旋轉和縮放操作,則進入transformResult方法處理後返回bitmap.此方法代碼好長,有一大堆的數學方法處理,有興趣的童鞋自行研究。當然如果picasso裏面的轉換不能滿足,也可以自己自定義,只要實現Transformation方法即可。

hunt方法分析完畢。繼續回到run方法裏面

@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);//獲取成功了

}

......省略一些無關代碼

}

查看Dispater的dispatchCompleter方法。dispatchFailed方法類似

void     dispatchComplete(BitmapHunter hunter) {

handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE,hunter));

}

查看handler處理

caseHUNTER_COMPLETE: {

BitmapHunter hunter = (BitmapHunter) msg.obj;

dispatcher.performComplete(hunter);

break;

}

performComplete方法。獲取bitmap完成後就寫入內存緩存.移除當前hunterMap集合中hunter對應的key。然後調用了batch方法

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");

}

}

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);

}

}

延遲200ms發送一個空消息

caseHUNTER_DELAY_NEXT_BATCH: {

dispatcher.performBatchComplete();

break;

}

performBatchComplete方法,複製到了一個copy集合中,發送一個消息到主線程的handler裏面

void      performBatchComplete() {

List copy =newArrayList<>(batch);

batch.clear();

mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE,copy));

logBatch(copy);

}

移步到Picasso裏面的主線程handler裏面

caseHUNTER_BATCH_COMPLETE: {

@SuppressWarnings("unchecked") List batch = (List) msg.obj;

//noinspection ForLoopReplaceableByForEach

for(inti =0,n = batch.size();i < n;i++) {

BitmapHunter hunter = batch.get(i);

hunter.picasso.complete(hunter);

}

break;

}

調用Picasso的complete方法

void       complete(BitmapHunter hunter) {

Action single = hunter.getAction();

List joined = hunter.getActions();

booleanhasMultiple = joined !=null&& !joined.isEmpty();

booleanshouldDeliver = single !=null|| hasMultiple;

//如果加載成功single則不爲null,shouldDeliver爲true

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

//如果一個url對應幾個imageview控件,則一一設置

for(inti =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);

}

}

然後調用deliverAction方法

private voiddeliverAction(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 newAssertionError("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());

}

}

}

回調ImageViewAction裏面的相關方法。這裏着重看complete方法

@Override

public void     complete(Bitmap result,Picasso.LoadedFrom from) {

if(result ==null) {

throw newAssertionError(

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;

booleanindicatorsEnabled =picasso.indicatorsEnabled;

//設置到bitmap到imageview上

PicassoDrawable.setBitmap(target,context,result,from,noFade,indicatorsEnabled);

//回調

if(callback!=null) {

callback.onSuccess();

}

}

最終調用PicassoDrawable裏面的setBitmap方法

static void     setBitmap(ImageView target,Context context,Bitmap bitmap,

Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {

Drawable placeholder = target.getDrawable();

if(placeholderinstanceofAnimationDrawable) {

((AnimationDrawable) placeholder).stop();

}

PicassoDrawable drawable =

new  PicassoDrawable(context,bitmap,placeholder,loadedFrom,noFade,debugging);

target.setImageDrawable(drawable);

}

至此完成

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章