Android 經常使用開源框架源碼解析 系列 (十一)picasso 圖片框架

1、前言
Picasso 強大的圖片加載緩存框架
 
api加載方式和Glide 相似,均是經過鏈式調用的方式進行調用 
 
1.一、做用
Picasso 管理整個圖片加載、轉換、緩存等策略
 
1.二、簡單調用:
Picasso .with(this 傳入一個單例,上下文).load(「url」/file文件/資源路徑) .into()
 
 1.2.1 、一些簡單的鏈式調用參數
.placeholder(R.drawable.xx)  //網絡未加載完成的時候顯示的本地資源圖片
.error(R.drawable.xx) //網絡加載失敗顯示的本地資源圖片
.resize(480,800)  //手動控制圖片寬高 ,對整個屏幕圖片進行設置像素設置
.centerCrop () //形成圖片拉伸扭曲的時候,將整個圖片充滿ImageView邊界,進行居中裁剪邊界
.rotate( 度數 0.0座標開始轉 ) //旋轉動畫效果
.priority()  //請求優先級,會影響請求執行順序,但不是百分之百保證的,只會往高優先級靠攏
.tag(「xxxx」)  //容許爲每個請求設置一個tag
    ps:若是不設置tag,在list列表請求的時候,會加載全部的圖片請求,就會形成Ui卡頓;
    而有了tag ,listView就能夠進行監聽然後進行不一樣的操做事件
 
能夠在滑動的setOnScrollListener()的監聽事件中 :
    在onScrollStateChanged()方法中經過判斷是否處於IDLE狀態,
若是處於IDLE狀態就恢復 tag(picasso.resumeTag) 繼續加載;
若是是在滑動狀態,中止圖片加載的請求 picasso.pauseTag
 
picasson的內存緩存 和 磁盤緩存 默認都會開啓的 ;可是不須要老是開啓,若是加載大圖片再緩存一份就很容易形成OOM
.memoryPolicy(MemoryPolicy.No_CACHE) //picasso 的內存緩存模式
.networkPolicy(MemoryPolicy.No_CACHE) //人爲禁掉 磁盤緩存
 
 
2、Picasso 源碼
    2.一、 引入
implementation 'com.squareup.picasso:picasso:2.71828'
or Maven:
<dependency>
  <groupId>com.squareup.picasso</groupId>
  <artifactId>picasso</artifactId>
  <version>2.71828</version>
</dependency>
    2.2 代碼混淆
    If you are using ProGuard you might need to add OkHttp's rules:  https://github.com/square/okhttp/#proguard
 
    2.3 源碼分析
    //    2.3.1 with()函數的進入-基礎配置工做,dispatch分發器配置,picasso對象配置
返回一個picasso對象的實例主旨功能 ,Picasso 入口函數
 
public void picasso() {
    Picasso.with(this) //使用單例模式構建保證在使用的時候整個module只有一個picasso存在
            .load("url")
            .into(imageView);
}
    with()://經過雙重鎖機制 保護線程的安全,經過Builder內部類的build()方法建立 Picasso對象
public static Picasso  with(Context context) {
  if (singleton == null) {
    synchronized (Picasso.class) {
      if (singleton == null) {
        singleton = new Builder(context).build();
      }
    }
  }
  return singleton;
}
 
build():
public Picasso build() {
  Context context = this.context;
    //實際圖片加載的過程
  if (downloader == null) {
    downloader = Utils.createDefaultDownloader(context);
——————————————————
ps: 經過反射查找是否有OkHttpClient庫,若是有就經過OkHttp庫 進行圖片下載和加載方式
Class.forName("com.squareup.okhttp.OkHttpClient");
return OkHttpLoaderCreator.create(context);
  }else {
//若沒有OkHttp庫就經過HttpURLConnection進行實現
return new UrlConnectionDownloader(context);
}
——————————————————
//經過LruCache 最近使用內存算法;進行內存緩存 策略,picasso 中指定了手機的內存爲15% 
——————————————————
ps:LruCache 實現了Lru算法複習;最近使用最少的緩存會被刪除;其實現會經過LinkedHashMap進行,每次調用get()、put()會將對象放到鏈表的尾端,每次put依然會將對象放到鏈表的尾部。當內存緩存達到最大值的時候,他就會將咱們的鏈表頭部的對象移除,以達到Lru算法的目的。
——————————————————
if (cache == null) {
    cache = new LruCache(context);
  }
//picasso 的線程池Executor 
  if (service == null) {
    service = new PicassoExecutorService();
——————————————————
ps:PicassoExecutorService 繼承自 ThreadPoolExecutor
private static final int DEFAULT_THREAD_COUNT = 3;
public ThreadPoolExecutor(int corePoolSize,  //核心線程數量,若是沒有任務的話,整個核心的corePoolSize也不會終止,除非認爲設置須要核心線程
                          int maximumPoolSize, //線程池中容許的最大的線程數量,線程池數量越大,開銷越大
                          long keepAliveTime, //當咱們線程池中的線程線程數目比核心線程多的時候,若是超過了keepAliveTime這個時間,那麼多餘的線程就會被回收,也就是被移出線程池
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue, //阻塞隊列,是一個線程安全隊列
                          ThreadFactory threadFactory, //用來構造線程池的工廠
                          RejectedExecutionHandler handler)  //任務阻塞的時候線程池的一種處理方式
  • 當有新任務的時候先觀察corePoolSize值
  • 而後觀察workQueue工做隊列是否已經滿了
  • 最好判斷是否有小於線程池的最大值
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
     ...
}
 
思考: 線程池中的線程是如何調度的呢?
  •     一、若是線程池中線程的數目少於corePoolSize; 這時候線程池會從新建立一個核心線程的,直到核心線程數量達到corePoolSize就不會再建立新核心線程了
  •     二、若是線程池中線程的數目大於或是等於corePoolSize,可是工做隊列workQueue沒有滿,那麼新的任務依然會放置到這個隊列當中,按照先進先出的原則來進行執行
  •     三、若是線程池中的線程數目大於等於corePoolSize,而且工做隊列workQueue滿了,可是總線程數目小於maximumPoolSize,那麼能夠直接建立一個線程,處理並添加到任務當中
  •    四、若是工做隊列滿了,而且線程池中線程的數目到達了最大數目maximumPoolSize的時候,這時候就須要用到RejectedExecutionHandler這個對象進行處理,默認處理方式是丟棄任務並拋出異常
——————————————————
  }
//配置請求轉換器,picasso 不進行轉換操做因此直接返回原來請求
  if (transformer == null) {
    transformer = RequestTransformer.IDENTITY;
  }
 
//新建一個保留緩存狀態對象
  Stats stats = new Stats(cache);
//主線程到子線程的切換經過 重要的Dispatcher 完成
  Dispatcherdispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
   //經過單例完成Picasso 對象,該Picasso 對象是加載圖片的入口同時
  return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
      defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
 
    2.3.2 、Dispatcher 完成線程切換
經過該類,在內部使用handler機制 ,完成主線程和子線程之間全部的切換。因此很是重要
Dispatcher()的構造方法:
Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
    Downloader downloader, Cache cache, Stats stats) {
//一、在構造方法中建立一個子線程,並開啓一個子線程 ,執行耗時操做
 
  this.dispatcherThread = new DispatcherThread();
——————————————————
ps: static class DispatcherThread extends HandlerThread,而HandlerThread又繼承自Thread本質上是一個線程類;
這個handlerThread會開啓一個新的線程,這個線程內部會有一個looper對象,經過looper對象會循環遍歷消息隊列
在其核心的 run()方法中 :
    //經過looper完成looper的建立,以及經過looper的循環方法來構造一個循環的線程。⚠️HandlerThread須要start()方法開啓線程
 
@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare(); //完成Looper對象的建立
    synchronized (this) {
        //利用同步鎖鎖住當前對象,經過myLooper()函數獲取當前looper並賦值
        mLooper = Looper.myLooper();
        notifyAll();//喚醒等待線程—wait()成對出現
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared(); //空實現方法
    Looper.loop();//開啓線程消息循環
    mTid = -1;
}
在getLooper()函數內 ,若是線程存活或是looper對象是空,則進入等待wait()狀態,表示進入阻塞階段,直到looper對象被成功建立同時調用了notifyAll()函數後就會喚醒先前的等待線程。
——————————————————
    …
//二、初始化兩個很重要的handler,一個是主線程中的handler ,一個是子線程的handler
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
——————————————————
ps:在其構造方法內傳入一個looper 和一個dispatcher 分發器,這個handler的做用是處理DispatcherThread 這個子線程當中的handler
public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
  super(looper);
  this.dispatcher = dispatcher;
}
——————————————————
this.downloader = downloader;
this.mainThreadHandler = mainThreadHandler;//主線程handler
——————————————————
ps:在前面的Dispatcherdispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);表達式中傳入的第三個參數就是表明主線程中的handler;該Handler是一個static靜態的handler對象,而且傳入一個主線程的looper對象,爲了防止內存泄漏的產生;在這裏若是定義成非靜態內部類的話,會持有外部累Picasso的引用,致使picasso對象沒法在該被回收的時候進行回收。
——————————————————
    …
//三、建立一個廣播接收者用於接收外部網絡環境的變化,內部經過切換子線程的數量完成理論操做
 
this.receiver = new NetworkBroadcastReceiver(this);//廣播接收者,用於監聽網絡變化等操做
    內部核心方法:onReceive(),監聽intent 並作出相應的操做
@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());
  }
}
dispatcher.dispatchNetworkStateChange():
//能夠發現使用的是handler經過sendMessage()方法進行消息的傳遞,交給HandleMessage()進行處理
void dispatchNetworkStateChange(NetworkInfo info) {
  handler.sendMessage(handler.obtainMessage(NETWORK_STATE_CHANGE, info));
}
在該方法中進行網絡變化的業務邏輯:
dispatcher.performNetworkStateChange(info);
void performNetworkStateChange(NetworkInfo info) {
   //線程池判斷,在adjustThreadCount()中根據不一樣的網絡類型設置線程的數量,根據用戶具體的場景和使用須要設置線程的數量,值得本身的代碼進行復刻
  if (service instanceof PicassoExecutorService) {
    ((PicassoExecutorService) service).adjustThreadCount(info);
         ...
}
 
    2.3.3 、NetworkRequestHandler處理 
//用來存放requestHandler ,根據具體的需求選擇相應的handler
List<RequestHandler> allRequestHandlers =
    new ArrayList<RequestHandler>(builtInHandlers + extraCount);
allRequestHandlers.add(new ContentStreamRequestHandler(context));
allRequestHandlers.add(new AssetRequestHandler(context));//用於處理asset 資源圖片,若是加載的圖片在Aset資源目錄下就
allRequestHandlers.add(new FileRequestHandler(context));//用於處理文件當中的圖片
allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));//若是圖片須要經過網絡下載就會經過這個handler進行處理
 
 
NetworkRequestHandler() 裏的核心方法:如何加載圖片請求
@Override 
public Result load(Request request, int networkPolicy) throws IOException {
    //開啓一個downloader下載器 完成下載
  Response response = downloader.load(request.uri, request.networkPolicy);
  if (response == null) {
    return null;
  }
  Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
  Bitmap bitmap = response.getBitmap();
  if (bitmap != null) {
    return new Result(bitmap, loadedFrom);
  }
  InputStream is = response.getInputStream();
  if (is == null) {
    return null;
  }
  // 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 && response.getContentLength() == 0) {
    Utils.closeQuietly(is);
    throw new ContentLengthException("Received response with 0 content-length header.");
  }
    //若是是從網絡下載圖片,同時獲取到的資源長度大於0 ,表示能夠調用成功的回調
  if (loadedFrom == NETWORK && response.getContentLength() > 0) {
    stats.dispatchDownloadFinished(response.getContentLength());
  }
  return new Result(is, loadedFrom);
}
//能夠發現該方法一樣是經過handle 發送消息來控制的,可是要注意的是這裏的這個handler不是前面的dispatchHandler中的handler了,這裏的這個handler是保存在state 類中的StatsHandler,傳入的是statsThread.getLooper()的handler
void dispatchDownloadFinished(long size) {
  handler.sendMessage(handler.obtainMessage(DOWNLOAD_FINISHED, size));
}
在case 中 經過 performDownloadFinished()方法 
 
stats.performDownloadFinished((Long) msg.obj);
void performDownloadFinished(Long size) {
  downloadCount++;  //當前下載數量加1 
  totalDownloadSize += size;//將總的下載文件的大小,加上本次下載文件大小的值
  averageDownloadSize = getAverage(downloadCount, totalDownloadSize);//計算平均下載的大小
}
 
   2.3.4 .load(「")
參數類型:
    一、Url 地址
    二、path文件路徑
    三、資源ID
    
//在load()方法中建立了RequestCreator對象,在RequestCreator對象內部又建立了一個RequestBuilder對象,經過Builder對象能夠進行鏈式調用配置更多參數
public RequestCreator load(String path) {
  if (path == null) {
    //建立圖片加載請求
    return new RequestCreator(this, null, 0);
 
ps:RequestCreator 對picasso進行賦值,並建立一個Request的Builder對象,該對象接收3個參數,url、資源佈局Id和一個默認的圖片配置
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);
}
 
  }
  if (path.trim().length() == 0) {
    throw new IllegalArgumentException("Path must not be empty.");
  }
  return load(Uri.parse(path));
}
 
   2.3.5 .into(「")
public void into(ImageView target, Callback callback) {
  long started = System.nanoTime();
   //一、用於檢驗當前線程是否實在主線程,非主線程拋出異常
  checkMain();
if (target == null) { //ImageView對象不能爲空
  throw new IllegalArgumentException("Target must not be null.");
}
    //二、建立Request的Builder 的內部類 data,hasImage()函數用於判斷 url 或是資源Id是否爲空
if (!data.hasImage()) {
  picasso.cancelRequest(target); //三、爲空則取消加載圖片請求
————————————————————————————
ps:
private void cancelExistingRequest(Object target) {
  checkMain();
    //Action是Request的封裝類,存儲了Picasso、request、memoryPolicy、networkPolicy等對象,封裝好後交給BitmapHunter這個Runnabel進行開啓線程下載
 一、targetToAction 是一個map對象,key是imageView而value就是請求包裝類action,取消請求就是把有關的target的信息刪除掉;
 
  Action action = targetToAction.remove(target);
  if (action != null) {
    action.cancel();
   二、使用了dispatch線程中的handler經過 sendMessage傳遞取消請求的請求,經過dispatcher的performCancel()進行取消:
dispatcher.dispatchCancel————>performCancel():
//BitmapHunter 是一個Runnable工具 能夠開啓線程,下載並處理Bitmap以及小量的圖片處理工做
BitmapHunterhunter = hunterMap.get(key);
if (hunter != null) {
  hunter.detach(action);
  if (hunter.cancel()) { //取消BitmapHunter操做
    hunterMap.remove(key);
  ...
}
    dispatcher.dispatchCancel(action);
  }
  if (target instanceof ImageView) {
   三、將target轉化成Imageview
    ImageView targetImageView = (ImageView) target;
    DeferredRequestCreator deferredRequestCreator =
        targetToDeferredRequestCreator.remove(targetImageView);
    //建立一個圖片加載請求的時候,還不能獲取到當前ImageView的寬和高,這時候就須要建立一個deferredRequestCreator類用來對ImageView的target去進行監聽,直到獲取到了ImageView的寬和高後就能夠從新執行請求建立了。因此說刪除請求也須要刪除掉deferredRequestCreator綁定的監聽事件
    if (deferredRequestCreator != null) {//Requestcreator 創造器的包裝類
      deferredRequestCreator.cancel();
    }
  }
}
————————————————————————
  if (setPlaceholder) {
四、經過setPlaceHolder()函數設置佔位符
    setPlaceholder(target, getPlaceholderDrawable());
  }
  return;
}
五、圖片加載是否延遲進行判斷
if (deferred) {
  if (data.hasSize()) {
    throw new IllegalStateException("Fit cannot be used with resize.");
  }
根據target寬高進行重新的測量
  int width = target.getWidth();
  int height = target.getHeight();
     ...
  data.resize(width, height);
}
六、建立圖片加載請求
Request request = createRequest(started);
ps:內部經過 Request request = data.build();建立Request對象並將這個Request對象進行轉換
Request transformed = picasso.transformRequest(request);
只有在建立Picasso的時候有關transform的時候就須要進行轉換的操做
 
//將ImageView和建立好的createKey值進行關聯,也就是將ImageView和Request關聯到一塊兒爲後續需求做準備
String requestKey = createKey(request);
 
七、判斷是否從內存緩存中讀取數據
if (shouldReadFromMemoryCache(memoryPolicy)) {
    八、獲取緩存當中的Bitmap
  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的構造方法中,將ImageView 添加到了Action.RequestWeakReference這個弱引用中,也就是說能夠在內存不足的時候會回收掉這個對象
Action action =
    new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
        errorDrawable, requestKey, tag, callback, noFade);
十、進行任務的提交
picasso.enqueueAndSubmit(action);
void enqueueAndSubmit(Action action) { //經過請求包裝類獲取到target 屬性
  Object target = action.getTarget();
  if (target != null && targetToAction.get(target) != action) { //判斷target和請求包裝Action類是否配套,若是不配套就取消,會將新的action 添加到 argetToAction這個Map當中
    // This will also check we are on the main thread.
    cancelExistingRequest(target);
    targetToAction.put(target, action);
  }
  submit(action);
}
void submit(Action action) { //內部經過dispatcher 分發器進行調度
  dispatcher.dispatchSubmit(action); //內部依然是經過handler進行消息的傳遞 REQUEST_SUBMIT
}
 
performSubmit():
void performSubmit(Action action, boolean dismissFailed) {
   一、對根據Action的標誌查詢是否已經存在於pausedTags這個set列表當中
  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對象,並調用attach()函數完成工做
BitmapHunter hunter = hunterMap.get(action.getKey());
if (hunter != null) {
  hunter.attach(action); //actions是在一個arrayList當中,保證了action 不會重複
  return;
}
   三、判斷線程池是否關閉了 ,若是關閉了就打印異常日誌
if (service.isShutdown()) {
  if (action.getPicasso().loggingEnabled) {
    log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
  }
  return;
}
    四、沒有關閉線程池的時候,經過forRequest()函數獲取到BitmapHunter對象,
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
————————————————————————————————————
ps:經過action、picasso 對象獲取到Request對象和requestHandler對象
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
    經過for循環遍歷全部的handler請求器,獲取到不一樣業務處理的請求業務處理器
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
    Action action) {
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
  RequestHandler requestHandler = requestHandlers.get(i);
  if (requestHandler.canHandleRequest(request)) { 
    //最終將BitmapHunter 子線程返回給orRequest()方法
    return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
  }
}
————————————————————————————————————
    future 對象用來保存結果
hunter.future = service.submit(hunter);
ps:將圖片請求放到線程池中進行
@Override
public Future<?> submit(Runnable task) {
將線程task封裝成PicassoFutureTask便於控制處理的線程類,並經過execute()函數開啓線程
  PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
  execute(ftask);
  return ftask;
}
 
hunterMap.put(action.getKey(), hunter);
if (dismissFailed) {
  failedActions.remove(action.getTarget());
}
 
if (action.getPicasso().loggingEnabled) {
  log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
}
 
思考:線程開啓後如何執行圖片加載?又是在何地方進行進行操做的呢?
private static final class PicassoFutureTask extends FutureTask<BitmapHunter>
    implements Comparable<PicassoFutureTask> {
  private final BitmapHunter hunter; //在PicassoFutrueTask中持有了BitmapHunter的引用
     
單個線程如何操做   ——BitmapHunter                                    
BitmapHunter 核心類: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);
    }
  } 
catch (Downloader.ResponseException e) {
      ...
  } finally {
    Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
  }
}
 
hunt():
Bitmap hunt() throws IOException {
  Bitmap bitmap = null;
    一、是否從內存緩存中讀取緩存進行判斷
  if (shouldReadFromMemoryCache(memoryPolicy)) {
    二、根據Action的key值從緩存中查找bitmap是否存在
    bitmap = cache.get(key);
    if (bitmap != null) {
   三、bitmap不爲空,修改stats的狀態
      stats.dispatchCacheHit();
      loadedFrom = MEMORY;
      if (picasso.loggingEnabled) {
        log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
      }
      return bitmap;
    }
  }
   四、若是緩存不存在就從網絡中加載圖片
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
RequestHandler.Result result =requestHandler.load(data, networkPolicy);//requestHandler是屬於networkRequestHandler
ps:RequestHandler的 NetworkRequestHandler實現爲例:
@Override
public Result load(Request request, int networkPolicy) throws IOException {
  Response response = downloader.load(request.uri, request.networkPolicy); //這裏的load
有兩個實現類OkHttpDownloader和UrlConnectionDownloader,也就是前者經過反射機制查找是否加載了OkHttp網絡框架,若是沒有使用OkHttp庫則直接使用google本身的HttpUrlConnection進行網絡加載
 
 
OkHttpDownloader 和 UrlConnectionDownloader 對於 load ()方法的區別
 
    a、OkHttpDownloader是如何下載圖片的
 
    
@Override 
public Response load(Uri uri, int networkPolicy) throws IOException {
    一、新建CacheControl 對象 用於對緩存進行處理,設置
  CacheControl cacheControl = null;
    …
    二、新建builder對象用來存放硬盤內存緩存的設置
CacheControl.Builder builder = new CacheControl.Builder();
   三、判斷是否從內存緩存中讀取數據
if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
  builder.noCache();
}
    四、若是設置不從硬盤緩存中讀取數據
if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
  builder.noStore();
}
    五、將build方法的返回值給cacheControl,由cacheControl變量設置OkHttp庫的設置
cacheControl = builder.build();
    六、新建Request對象併爲其url賦值
Request.Builder builder = new Request.Builder().url(uri.toString());
if (cacheControl != null) {
    設置Okhttp 緩存策略的頭部
  builder.cacheControl(cacheControl);
}
    七、調用OkHttp同步請求 獲取response對象
com.squareup.okhttp.Response response = client.newCall(builder.build()).execute();
int responseCode = response.code();
    八、針對響應碼進行判斷 若是大於等於300就關閉響應體
if (responseCode >= 300) {
  response.body().close();
  throw new ResponseException(responseCode + " " + response.message(), networkPolicy,
      responseCode);
}
    九、若是小於300就返回response 
  boolean fromCache = response.cacheResponse() != null;
  ResponseBody responseBody = response.body();
  return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength());
}
  b、UrlConnectionDownloader是如何下載圖片的
@Override 
public Response load(Uri uri, int networkPolicy) throws IOException {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    installCacheIfNeeded(context);
  }
   一、新建HttpURLConnection 對象
  HttpURLConnection connection = openConnection(uri);
  connection.setUseCaches(true); //開啓緩存功能
    二、判斷是否僅在離線狀況下使用緩存
if (networkPolicy != 0) {
  String headerValue;
 
  if (NetworkPolicy.isOfflineOnly(networkPolicy)) {
    headerValue = FORCE_CACHE;
  } else {
    三、判斷是否從內存緩存中讀取
if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
    不從緩存中讀取 直接添加no-cache 標籤
  builder.append("no-cache");
}
    四、判斷是否從硬盤緩存中讀取數據
if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
  if (builder.length() > 0) {
    builder.append(',');
  }
    不從硬盤緩存中讀取數據添加 no-store標籤
  builder.append("no-store");
}
 
headerValue = builder.toString();
    五、將獲取到的全部緩存信息添加到Cache-Control 頭部信息中
connection.setRequestProperty("Cache-Control", headerValue);
    六、跟OkHttp同樣獲取responseCode響應碼,大於300關閉鏈接,小於300返回response
int responseCode = connection.getResponseCode();
if (responseCode >= 300) { 
  connection.disconnect();
  throw new ResponseException(responseCode + " " + connection.getResponseMessage(),
      networkPolicy, responseCode);
}
long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));
return new Response(connection.getInputStream(), fromCache, contentLength);
}
 
dispatcher.dispatchComplete():
void performComplete(BitmapHunter hunter) {
    一、首先對緩存策略進行判斷,若是開啓內測緩存就
  if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
        將結果保存到cache緩存當中
    cache.set(hunter.getKey(), hunter.getResult());
  }
   二、從map中移除BitmapHunter對應的key值,由於這會兒請求已經完成能夠刪除對應的BitmapHunter中的數據不然會重複請求
  hunterMap.remove(hunter.getKey());
  batch(hunter);
——————————————————
ps:batch()
private void batch(BitmapHunter hunter) {
    //一、判斷bitmapHunter 是否已經取消
  if (hunter.isCancelled()) {
    return;
  }
    //二、沒有取消就將這個hunter存放在batch 這個list 集合當中
  batch.add(hunter);
  if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
    //三、發送一個空消息 給handler進行處理
    handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
  }
}
——————————————————
void performBatchComplete() {
    //一、首先獲取存放BitmapHunter的集合
  List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
   //二、清理整個batch集合
  batch.clear();
    //三、給主線程發送消息 處理事件
  mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
  logBatch(copy);
}
——————————————————
   ps:在handleMessage()中遍歷BitmapHunter 並使用picasso.complete()函數進行操做
for (int i = 0, n = batch.size(); i < n; i++) {
  BitmapHunter hunter = batch.get(i);
  hunter.picasso.complete(hunter);
}
    …
    //根據BitmapHunter 獲取Uri信息、異常信息、bitmap信息等
Uri uri = hunter.getData().uri;
Exception exception = hunter.getException();
Bitmap result = hunter.getResult();
LoadedFrom from = hunter.getLoadedFrom();
 
if (single != null) {
    //分發圖片請求的包裝類Action 
  deliverAction(result, from, single);
    //在deliverAction()方法中 ,當Action沒有被取消,以及bitmap不爲空的時候,就會調用  action.complete(result, from)完成一些操做;
    在complete的實現類ImageViewAction的complete()中,會將target轉換成ImageView,在後面調用
PicassoDrawable的setBitmap()方法中使用setImageDrawable(drawable)方法設置圖片,最終調用ImageView進行設置圖片的操做
}
——————————————————
    ...
  if (hunter.getPicasso().loggingEnabled) {
    log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
  }
}
相關文章
相關標籤/搜索