Picasso流程解析

Picasso流程解析

首先從調(diào)用代碼看起

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

Picasso.with(context)
.load(url)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.resizeDimen(R.dimen.list_detail_image_size,R.dimen.list_detail_image_size)
.centerInside()
.into(holder.image);

很顯然是單例模式

  public static Picasso with(Context context) {
    if (singleton == null) {
        Class var1 = Picasso.class;
        synchronized(Picasso.class) {
            if (singleton == null) {
                singleton = (new Picasso.Builder(context)).build();
            }
        }
    }

    return singleton;
 }

再看load方法,對參數(shù)做了檢查,調(diào)用了一個(gè)重載方法,返回的是一個(gè)RequestCreator對象

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

String轉(zhuǎn)換為Uri,直接生成一個(gè)RequestCreator對象,并把uri放入里面然后返回。后面的方法就在該對象里面調(diào)用了

public  RequestCreator load(@Nullable Uri uri) {
      return new RequestCreator(this,uri,0);
}

而這個(gè)uri。則放入到data里面,這個(gè)data是,Request的一個(gè)內(nèi)部類Builder.

RequestCreator(Picasso picasso, Uri uri, int resourceId) {
    if (picasso.shutdown) {
        throw new IllegalStateException("Picasso instance already shut down. Cannot submit new requests.");
    } else {
        this.picasso = picasso;
        this.data = new Builder(uri, resourceId, picasso.defaultBitmapConfig);
    }
}

再往下看。placeholder方法就是設(shè)置還未下載圖片之前占位資源id,這樣先顯示的就是這張圖片

public RequestCreator placeholder(@DrawableRes int placeholderResId) {
        //默認(rèn)這個(gè)值為true,如果調(diào)用noPlaceholder方法則為false
       if(!setPlaceholder) {
               throw new IllegalStateException("Already explicitly declared as no placeholder.");

         }

        if(placeholderResId ==0) {
              throw new IllegalArgumentException("Placeholder image resource invalid.");
          }
            //如果設(shè)置過placeholderDrawable,再調(diào)用這個(gè)方法就會拋異常。
          if(placeholderDrawable!=null) {
             throw new IllegalStateException("Placeholder image already set.");
          }

         this.placeholderResId= placeholderResId;

         return this;

    }

error方法設(shè)置如果發(fā)生錯(cuò)誤是顯示資源Id,和placeholder方法一樣。

/** 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;

}

接下來是resizeDimen方法,獲取寬高。

 public RequestCreator resizeDimen(int targetWidthResId, int targetHeightResId) {
        Resources resources = this.picasso.context.getResources();
        int targetWidth = resources.getDimensionPixelSize(targetWidthResId);
        int targetHeight = resources.getDimensionPixelSize(targetHeightResId);
        return this.resize(targetWidth, targetHeight);
    }

然后把寬高放到data里面,這個(gè)data是Request的內(nèi)部類Builder.

public RequestCreator resize(int targetWidth, int targetHeight) {
        this.data.resize(targetWidth, targetHeight);
        return this;
    }

centerInside方法,也是調(diào)用data的方法。

 public RequestCreator centerInside() {
        this.data.centerInside();
        return this;
    }

最后看into方法。調(diào)用重載方法

 public void into(ImageView target) {
        this.into(target, (Callback)null);
  }

這個(gè)方法代碼比較長。

首先檢查是否在主線程調(diào)用。如果不是則直接拋異常。因?yàn)楹竺嬉O(shè)置控件占位圖片,或者從緩存中讀取圖片設(shè)置給控件,所有該方法一定要在主線程調(diào)用。

然后一系列參數(shù)判斷。不過默認(rèn)的值都會走到生成reqeust
利用request的信息生成key,因?yàn)榫彺婢褪歉鶕?jù)該key去獲取的。
默認(rèn)內(nèi)存緩存是開啟的。從內(nèi)存緩存中獲取,獲取到了直接設(shè)置返回,啥事也就沒有了。
沒有緩存則判斷是否設(shè)置了占位圖片,如果設(shè)置了則先顯示占位圖片,根據(jù)生成一個(gè)Action.調(diào)用picasso的方法添加

public void into(ImageView target, Callback callback) {
        long started = System.nanoTime();
        //檢查是否在主線程。不是則拋出異常。
        Utils.checkMain();
        if (target == null) {
            throw new IllegalArgumentException("Target must not be null.");
        //這里是查看data里面是否有uri.或者resourceId.因?yàn)榍懊鏄?gòu)造RequestCreator的時(shí)候把uri傳給了data.所以hasImage返回true
        } else if (!this.data.hasImage()) {
            this.picasso.cancelRequest(target);
            if (this.setPlaceholder) {
                PicassoDrawable.setPlaceholder(target, this.getPlaceholderDrawable());
            }

        } else {
            //默認(rèn)這個(gè)值是false.
            if (this.deferred) {
                if (this.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 (this.setPlaceholder) {
                        PicassoDrawable.setPlaceholder(target, this.getPlaceholderDrawable());
                    }
                    this.picasso.defer(target, new DeferredRequestCreator(this, target, callback));
                    return;
                }

                this.data.resize(width, height);
            }
            //利用data創(chuàng)造一個(gè)Request.因?yàn)橹霸O(shè)置的屬性都設(shè)置到data里面。所以這里創(chuàng)建的時(shí)候傳到了Request里面
            Request request = this.createRequest(started);
            //利用request的一些信息生成key.
            String requestKey = Utils.createKey(request);
            //如果設(shè)置了NO_CACHE,則不從內(nèi)存緩存中獲取。否則先從內(nèi)存緩存中獲取。默認(rèn)緩存是LruCache
            if (MemoryPolicy.shouldReadFromMemoryCache(this.memoryPolicy)) {
                Bitmap bitmap = this.picasso.quickMemoryCacheCheck(requestKey);
                //如果緩存中有。則取消請求。設(shè)置到目標(biāo)控件上
                if (bitmap != null) {
                    this.picasso.cancelRequest(target);
                    PicassoDrawable.setBitmap(target, this.picasso.context, bitmap, LoadedFrom.MEMORY, this.noFade, this.picasso.indicatorsEnabled);
                    if (this.picasso.loggingEnabled) {
                        Utils.log("Main", "completed", request.plainId(), "from " + LoadedFrom.MEMORY);
                    }

                    if (callback != null) {
                        callback.onSuccess();
                    }

                    return;
                }
            }
            //如果設(shè)置了占位圖片,先設(shè)置占位圖片
            if (this.setPlaceholder) {
                PicassoDrawable.setPlaceholder(target, this.getPlaceholderDrawable());
            }
            //生成一個(gè)Action
            Action action = new ImageViewAction(this.picasso, target, request, this.memoryPolicy, this.networkPolicy, this.errorResId, this.errorDrawable, requestKey, this.tag, callback, this.noFade);
            //添加到隊(duì)列并且提交
            this.picasso.enqueueAndSubmit(action);
        }
    }

小結(jié):with方法是創(chuàng)建一個(gè)Picasso單例出來,load方法是創(chuàng)建一個(gè)RequestCreator對象出來。后面的所有鏈?zhǔn)秸{(diào)用就都是在RequestCreator對象里面。RequestCreator對象主要是接收參數(shù),并且校驗(yàn)參數(shù)。并且從內(nèi)存緩存里面,如果存在則不用去網(wǎng)絡(luò)請求,也就沒RequestAction的什么事了。如果不存在,把一些必要的參數(shù)放到Action對象,然后提交出去。

下面再看看怎么處理網(wǎng)絡(luò)請求的,接著上面從PicassoenqueueAndSubmit看起

void enqueueAndSubmit(Action action) {
        Object target = action.getTarget();
        //如果已經(jīng)有了該action.則取消。再重新放入
        if (target != null && this.targetToAction.get(target) != action) {
            this.cancelExistingRequest(target);
            this.targetToAction.put(target, action);
        }

        this.submit(action);
    }

void submit(Action action) {
        this.dispatcher.dispatchSubmit(action);
 }

調(diào)用Dispatcher對象的dispatchSubmit方法,發(fā)送一個(gè)what為1的message,該handler是一個(gè)內(nèi)部類,并且不是獲取主線程的loop。而是獲取內(nèi)部類DispatcherThreadloop。該類繼承HandlerThread。也就是說handler是在子線程中處理Message的。

 void dispatchSubmit(Action action) {
        this.handler.sendMessage(this.handler.obtainMessage(1, action));
    }

handlerMessage方法里面處理消息為1的地方,調(diào)用了performSubmit方法。此時(shí)已經(jīng)切換到子線程中運(yùn)行了

public void handleMessage(final Message msg) {
        Object tag;
        BitmapHunter hunter;
        Action action;
        switch(msg.what) {
        case 1:
            action = (Action)msg.obj;
            this.dispatcher.performSubmit(action);
            break;
        case 2:

調(diào)用重載方法

 void performSubmit(Action action) {
        this.performSubmit(action, true);
    }

注釋1.判斷當(dāng)前action的是否暫停了。如果暫停了。就不去請求。這個(gè)在配合列表使用更好。因?yàn)楫?dāng)滑動(dòng)列表時(shí),會產(chǎn)生很多請求,這個(gè)時(shí)候如果滑動(dòng)的時(shí)候調(diào)用pauseTag方法,則就不會請求服務(wù)器。滑動(dòng)完成后調(diào)用resumeTag,又會去請求服務(wù)器

注釋2 判斷是否有相同的action。這樣就不用重復(fù)添加。
注釋3則利用action,和其他信息生成一個(gè)BitmapHunter對象。該對象繼承了Runnable對象。并且把該對象放入線程池中。既然是Runnable對象。那就看run方法

  void performSubmit(Action action, boolean dismissFailed) {
        //1判斷集合Set中是否存在,由于沒設(shè)置過,因此走else.如果設(shè)置了。則就不會請求。如果在列表中滑動(dòng)會生成很多action。這樣不可見的ImageView就可以設(shè)置pausedTag。
        if (this.pausedTags.contains(action.getTag())) {
            this.pausedActions.put(action.getTarget(), action);
            if (action.getPicasso().loggingEnabled) {
                Utils.log("Dispatcher", "paused", action.request.logId(), "because tag '" + action.getTag() + "' is paused");
            }

        } else {
            //2 由于沒設(shè)置過,故為null
            BitmapHunter hunter = (BitmapHunter)this.hunterMap.get(action.getKey());
            if (hunter != null) {
                hunter.attach(action);
            //線程池是否關(guān)閉了
            } else if (this.service.isShutdown()) {
                if (action.getPicasso().loggingEnabled) {
                    Utils.log("Dispatcher", "ignored", action.request.logId(), "because shut down");
                }

            } else {
                //3生成了一個(gè)BitmapHunter對象,該對象繼承了Runnable
                hunter = BitmapHunter.forRequest(action.getPicasso(), this, this.cache, this.stats, action);
                //提交到線程池
                hunter.future = this.service.submit(hunter);
                
                //把a(bǔ)ction的key 和hunter綁定在集合
                this.hunterMap.put(action.getKey(), hunter);
                if (dismissFailed) {
                    this.failedActions.remove(action.getTarget());
                }

                if (action.getPicasso().loggingEnabled) {
                    Utils.log("Dispatcher", "enqueued", action.request.logId());
                }

            }
        }
    }

BitmapHunter對象的run方法,設(shè)置線程名。然后調(diào)用hunt方法去獲取。然后返回相應(yīng)的回調(diào)。如果出現(xiàn)了異常,根據(jù)不同異常執(zhí)行重試或者回調(diào)失敗

 public void run() {
        try {
            //設(shè)置線程名
            updateThreadName(this.data);
            if (this.picasso.loggingEnabled) {
                Utils.log("Hunter", "executing", Utils.getLogIdsForHunter(this));
            }

            this.result = this.hunt();
            if (this.result == null) {
                this.dispatcher.dispatchFailed(this);
            } else {
                this.dispatcher.dispatchComplete(this);
            }
        } catch (ResponseException var10) {
            if (!var10.localCacheOnly || var10.responseCode != 504) {
                this.exception = var10;
            }

            this.dispatcher.dispatchFailed(this);
        } catch (ContentLengthException var11) {
            this.exception = var11;
            //重新提交請求。
            this.dispatcher.dispatchRetry(this);
        } catch (IOException var12) {
            this.exception = var12;
            //重新提交請求。
            this.dispatcher.dispatchRetry(this);
        } catch (OutOfMemoryError var13) {
            StringWriter writer = new StringWriter();
            this.stats.createSnapshot().dump(new PrintWriter(writer));
            this.exception = new RuntimeException(writer.toString(), var13);
            this.dispatcher.dispatchFailed(this);
        } catch (Exception var14) {
            this.exception = var14;
            this.dispatcher.dispatchFailed(this);
        } finally {
            Thread.currentThread().setName("Picasso-Idle");
        }

    }

hunt方法。這個(gè)方法比較長。看主要部分的,這里也先是取緩存中獲取。如果存在,則直接返回。如果不存在,則調(diào)用requestHandler去處理。
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
//1 如果可以從緩存中獲取。去緩存獲取。
if (MemoryPolicy.shouldReadFromMemoryCache(this.memoryPolicy)) {
bitmap = this.cache.get(this.key);
if (bitmap != null) {
this.stats.dispatchCacheHit();
this.loadedFrom = LoadedFrom.MEMORY;
if (this.picasso.loggingEnabled) {
Utils.log("Hunter", "decoded", this.data.logId(), "from cache");
}

                return bitmap;
            }
        }

        this.data.networkPolicy = this.retryCount == 0 ? NetworkPolicy.OFFLINE.index : this.networkPolicy;
        //2 通過requestHandler去獲取
        Result result = this.requestHandler.load(this.data, this.networkPolicy);
       ...
        }

        return bitmap;
    }

requestHandler是創(chuàng)建BitmapHunter的時(shí)候傳進(jìn)來的。這里獲取Picasso里面RequestHandler的集合。而requestHandlers里面有什么呢?

  static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) {
        Request request = action.getRequest();
        List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
        int i = 0;

        for(int count = requestHandlers.size(); i < count; ++i) {
            RequestHandler 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);
    }

創(chuàng)建Picasso的時(shí)候就已經(jīng)放置很多RequestHandler在里面,根據(jù)load方法傳進(jìn)來的字符串資源分別對應(yīng)不一樣的處理對象。這樣我們就可以在load方法里面?zhèn)魅胭Y源文件或者文件路徑或者網(wǎng)絡(luò)url等等

        List<RequestHandler> allRequestHandlers = new ArrayList(builtInHandlers + extraCount);
        allRequestHandlers.add(new ResourceRequestHandler(context));
        if (extraRequestHandlers != null) {
            allRequestHandlers.addAll(extraRequestHandlers);
        }

        allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
        allRequestHandlers.add(new MediaStoreRequestHandler(context));
        allRequestHandlers.add(new ContentStreamRequestHandler(context));
        allRequestHandlers.add(new AssetRequestHandler(context));
        allRequestHandlers.add(new FileRequestHandler(context));
        allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
        this.requestHandlers = Collections.unmodifiableList(allRequestHandlers);

由于我們傳進(jìn)來的是url。所以這里的requestHandler就是NetworkRequestHandler處理

NetworkRequestHandler方法處理,這個(gè)downloader也是Picasso類里面創(chuàng)建的。并且里面用的是OkHttp

 public Result load(Request request, int networkPolicy) throws IOException {
        Response response = this.downloader.load(request.uri, request.networkPolicy);
        if (response == null) {
            return null;
        } else {
            LoadedFrom loadedFrom = response.cached ? LoadedFrom.DISK : LoadedFrom.NETWORK;
            Bitmap bitmap = response.getBitmap();
            //返回bitmap
            if (bitmap != null) {
                return new Result(bitmap, loadedFrom);
            } else {
                InputStream is = response.getInputStream();
                if (is == null) {
                    return null;
                } else if (loadedFrom == LoadedFrom.DISK && response.getContentLength() == 0L) {
                    Utils.closeQuietly(is);
                    throw new NetworkRequestHandler.ContentLengthException("Received response with 0 content-length header.");
                } else {
                    if (loadedFrom == LoadedFrom.NETWORK && response.getContentLength() > 0L) {
                        this.stats.dispatchDownloadFinished(response.getContentLength());
                    }

                    return new Result(is, loadedFrom);
                }
            }
        }
    }

繼續(xù)看hunt方法

 Bitmap hunt() throws IOException {
        Bitmap bitmap = null;
       ...
        Result result = this.requestHandler.load(this.data, this.networkPolicy);
        if (result != null) {
            this.loadedFrom = result.getLoadedFrom();
            this.exifRotation = result.getExifOrientation();
            bitmap = result.getBitmap();
            if (bitmap == null) {
                InputStream is = result.getStream();

                try {
                    bitmap = decodeStream(is, this.data);
                } finally {
                    Utils.closeQuietly(is);
                }
            }
        }

        if (bitmap != null) {
            if (this.picasso.loggingEnabled) {
                Utils.log("Hunter", "decoded", this.data.logId());
            }

            this.stats.dispatchBitmapDecoded(bitmap);
            //如果需要縮放或者旋轉(zhuǎn)處理。
            if (this.data.needsTransformation() || this.exifRotation != 0) {
                Object var10 = DECODE_LOCK;
                synchronized(DECODE_LOCK) {
                    if (this.data.needsMatrixTransform() || this.exifRotation != 0) {
                        bitmap = transformResult(this.data, bitmap, this.exifRotation);
                        if (this.picasso.loggingEnabled) {
                            Utils.log("Hunter", "transformed", this.data.logId());
                        }
                    }

                    if (this.data.hasCustomTransformations()) {
                        bitmap = applyCustomTransformations(this.data.transformations, bitmap);
                        if (this.picasso.loggingEnabled) {
                            Utils.log("Hunter", "transformed", this.data.logId(), "from custom transformations");
                        }
                    }
                }

                if (bitmap != null) {
                    this.stats.dispatchBitmapTransformed(bitmap);
                }
            }
        }

        return bitmap;
    }

繼續(xù)往前看hunt調(diào)用的地方,如果已經(jīng)獲取到bitmap了,調(diào)用dispatcherdispatchComplete方法

     public void run() {
        try {
            //設(shè)置線程名
            updateThreadName(this.data);
            if (this.picasso.loggingEnabled) {
                Utils.log("Hunter", "executing", Utils.getLogIdsForHunter(this));
            }

            this.result = this.hunt();
            if (this.result == null) {
                this.dispatcher.dispatchFailed(this);
            } else {
                this.dispatcher.dispatchComplete(this);
            }
        } 
        ...

    }

DispatcherdispatchComplete方法

void dispatchComplete(BitmapHunter hunter) {
        this.handler.sendMessage(this.handler.obtainMessage(4, hunter));
    }

  case 4:````````
   hunter = (BitmapHunter)msg.obj;
   this.dispatcher.performComplete(hunter);
    break;

調(diào)用performComplete方法,設(shè)置了緩存

 void performComplete(BitmapHunter hunter) {
        if (MemoryPolicy.shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
            this.cache.set(hunter.getKey(), hunter.getResult());
        }

        this.hunterMap.remove(hunter.getKey());
        this.batch(hunter);
        if (hunter.getPicasso().loggingEnabled) {
            Utils.log("Dispatcher", "batched", Utils.getLogIdsForHunter(hunter), "for completion");
        }

    }

又發(fā)了一個(gè)消息

  private void batch(BitmapHunter hunter) {
        if (!hunter.isCancelled()) {
            this.batch.add(hunter);
            if (!this.handler.hasMessages(7)) {
                this.handler.sendEmptyMessageDelayed(7, 200L);
            }

        }
    }

處理消息。調(diào)用了performBatchComplete

 public void handleMessage(final Message msg) {
        Object tag;
        BitmapHunter hunter;
        Action action;
        switch(msg.what) {
       ...
        case 7:
            this.dispatcher.performBatchComplete();
            break;

發(fā)到主線程中去了,這主線程是Pacasso里面的

  void performBatchComplete() {
        List<BitmapHunter> copy = new ArrayList(this.batch);
        this.batch.clear();
        this.mainThreadHandler.sendMessage(this.mainThreadHandler.obtainMessage(8, copy));
        this.logBatch(copy);
    }

Picasso里面的handleMessage方法

  public void handleMessage(Message msg) {
           ...
            case 8:
                batch = (List)msg.obj;
                i = 0;

                for(n = batch.size(); i < n; ++i) {
                    BitmapHunter hunter = (BitmapHunter)batch.get(i);
                    hunter.picasso.complete(hunter);
                }

                return;

調(diào)用了complete方法,我們這里的single是個(gè)ImageViewAction.后deliverAction會調(diào)用ImageViewAction里面的complete方法

 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) {
            Uri uri = hunter.getData().uri;
            Exception exception = hunter.getException();
            Bitmap result = hunter.getResult();
            Picasso.LoadedFrom from = hunter.getLoadedFrom();
            if (single != null) {
                this.deliverAction(result, from, single);
            }

            if (hasMultiple) {
                int i = 0;

                for(int n = joined.size(); i < n; ++i) {
                    Action join = (Action)joined.get(i);
                    this.deliverAction(result, from, join);
                }
            }

            if (this.listener != null && exception != null) {
                this.listener.onImageLoadFailed(this, uri, exception);
            }

        }
    }

ImageViewActioncomplete方法。

   public void complete(Bitmap result, LoadedFrom from) {
        if (result == null) {
            throw new AssertionError(String.format("Attempted to complete action with no result!\n%s", this));
        } else {
            ImageView target = (ImageView)this.target.get();
            if (target != null) {
                Context context = this.picasso.context;
                boolean indicatorsEnabled = this.picasso.indicatorsEnabled;
                PicassoDrawable.setBitmap(target, context, result, from, this.noFade, indicatorsEnabled);
                if (this.callback != null) {
                    this.callback.onSuccess();
                }

            }
        }
    }


}

至此完成
小結(jié):Dispatcher里面有個(gè)DispatcherThread線程,該線程繼承HandlerThread類,里面的DispatcherHandler類用的就是DispatcherThreadloop.這樣就可以方便的把代碼切換到子線程。主要的工作是在BitmapHunter類的run方法。并且里面的下載用的是Okhttp。下載完成后如果設(shè)置了大小的,或者自定義處理的。都在獲取后操作bitmap的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容