Glide into(ImageView)源碼分析

Glide.with(this).load(url).into(imageView);

經(jīng)過 Glide.with(xxx).load(xxx) 之后饭于,最終會得到 RequestBuilder<Drawable>蜀踏。因此 Glide.with(Context).into(ImageView)最后一步是調(diào)用 RequestBuilder的into(ImageView)方法。

1掰吕、into(ImageView iv)

@SuppressWarnings({"unused", "WeakerAccess"})
public class RequestBuilder{


  //加載資源到ImageView控件中果覆,取消view對應的其他加載,同時釋放已經(jīng)已經(jīng)設置到view的資源
  @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    //設置BaseRequestOptions(請求配置)
    BaseRequestOptions<?> requestOptions = this;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {

      switch (view.getScaleType()) {
        case CENTER_CROP:
          //
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        ...
        default:
          // Do nothing.
      }
    }

    //glideContext.buildImageViewTarget(view, transcodeClass)
    //會返回一個DrawableImageViewTarget或者BitmapImageViewTarget
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

  
  private <Y extends Target<TranscodeType>> Y into(...) {
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }


    //構(gòu)建請求
    Request request = buildRequest(target, targetListener, options, callbackExecutor);

    //圖片控件的上一個圖片請求
    Request previous = target.getRequest();

    //如果本次請求和上一個請求相同畴栖,
    //而且本次請求不需要跳過緩存或上次請求還沒完成
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();//釋放本次請求
      //如果上次的請求已經(jīng)完成随静,重新執(zhí)行一次
      //如果上次的請求失敗,則重新開始
      //如果上次請求還在執(zhí)行中吗讶,那讓它繼續(xù)跑
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }

    
    requestManager.clear(target);//清除target中舊的請求
    target.setRequest(request);//給target綁定新的請求
    requestManager.track(target, request);//執(zhí)行請求

    return target;
  }

  //圖片請求不需要內(nèi)存緩存同時圖片控件對應的上一個請求已經(jīng)完成燎猛,則返回true
  private boolean isSkipMemoryCacheWithCompletePreviousRequest(
      BaseRequestOptions<?> options, Request previous) {
    return !options.isMemoryCacheable() && previous.isComplete();
  }

}

into()方法返回參數(shù)是ViewTarget,ViewTarget是個抽象類照皆,負責加載Bitmap到View上重绷。

  1. 配置requestOptions的Scaletype類型。
  2. 調(diào)用buildRequest方法構(gòu)建Request膜毁,并把Request設置給ViewTarget昭卓。
  3. 調(diào)用requestManager.track()方法執(zhí)行請求。

2瘟滨、buildRequest() 構(gòu)建請求

這里會涉及到三個request
1候醒、SingleRequest 負責執(zhí)行請求并將結(jié)果反映到 Target 上
2、ErrorRequestCoordinator 負責協(xié)調(diào)圖片請求的Request和請求失敗時的errorRequest
3杂瘸、ThumbnailRequestCoordinator 負責協(xié)調(diào)原圖加載的request和縮略圖加載request

2.1 buildRequestRecursive()
public class RequestBuilder {

  //構(gòu)建Request的入口
  private Request buildRequest(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {

    //直接調(diào)用buildRequestRecursive倒淫,遞歸調(diào)用創(chuàng)建Request 
    return buildRequestRecursive(...);
  }

  //遞歸調(diào)用,這里會分配一般情況下圖片請求Request和請求失敗的調(diào)用的errorRequest
  /**
    * @params targetListener       圖片請求回調(diào)败玉,可能是null
    * @params transitionOptions  過度動畫配置
    * @params priority                  請求的優(yōu)先級
    * @params overrideWidth       指定圖片加載的寬度
    * @params overrideHeight      指定圖片加載的高度
    * @params callbackExecutor  線程調(diào)度器
    */
  private Request buildRequestRecursive(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,//null
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth, //override(int width, int height)
      int overrideHeight,//override(int width, int height)
      BaseRequestOptions<?> requestOptions
      Executor callbackExecutor) {

    /*  如果調(diào)用了error(@Nullable RequestBuilder<TranscodeType> errorBuilder)這個方法敌土,errorBuilder才不為    
     *  空镜硕,比如
     *       Glide.with(context)
     *           .load((Object) null)
     *           .error(
     *                Glide.with(context)
     *                .load(errorModel)
     *                .listener(requestListener))
     *           .submit();
    */

    //ErrorRequestCoordinator 主要用來協(xié)調(diào)圖片請求的Request和請求失敗時的errorRequest
    ErrorRequestCoordinator errorRequestCoordinator = null;
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }

    //圖片請求的Request,調(diào)用buildThumbnailRequestRecursive
    Request mainRequest =
        buildThumbnailRequestRecursive(...);

    if (errorRequestCoordinator == null) {
      return mainRequest;
    }

    ...
    
    //構(gòu)建圖片請求失敗時的調(diào)用的errorRequest
    Request errorRequest =
        errorBuilder.buildRequestRecursive(...);
    
    //內(nèi)部協(xié)調(diào)mainRequest和errorRequest的調(diào)用流程
    errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
  }
}

第一步判斷是否調(diào)用了error(@Nullable RequestBuilder<TranscodeType> errorBuilder)這個方法返干,如果有兴枯,則先構(gòu)建ErrorRequestCoordinator,然后調(diào)用buildThumbnailRequestRecursive方法矩欠,最后構(gòu)建errorRequest财剖。

2.2 buildThumbnailRequestRecursive()
//遞歸調(diào)用,這里會分配一般情況下圖片請求Request和縮略圖請求的thumbnailRequest
private Request buildThumbnailRequestRecursive(...) {
    if (thumbnailBuilder != null) {
      //調(diào)用了thumbnail(@Nullable RequestBuilder<TranscodeType> thumbnailRequest) 方法晚顷,thumbnailBuilder 才不為空       
      //原圖的請求和縮略圖的請求可以不是同一張圖片峰伙,看調(diào)用者的設置
     
      ...

      //ThumbnailRequestCoordinator用來協(xié)調(diào)兩個請求疗疟,因為有的請求需要同時加載原圖和縮略圖
      //如果原圖已經(jīng)加載完成该默,那么縮略圖不會再加載
      ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
      //原圖的請求
      Request fullRequest =
          obtainRequest(...);
      isThumbnailBuilt = true;

      //縮略圖的請求
      Request thumbRequest =
          thumbnailBuilder.buildRequestRecursive(...);

      isThumbnailBuilt = false;
      //配置fullRequest和thumbRequest,內(nèi)部協(xié)調(diào)兩個請求的調(diào)用
      coordinator.setRequests(fullRequest, thumbRequest);
      return coordinator;
    } 
      ...
  }

buildThumbnailRequestRecursive方法構(gòu)建圖片的請求時策彤,會判斷是否有調(diào)用了thumbnail方法栓袖,如果有,就創(chuàng)建ThumbnailRequestCoordinator店诗, 然后創(chuàng)建原圖請求和縮略圖請求裹刮。

2.3 obtainRequest()
  private Request obtainRequest(...) {

    //返回SingleRequest的實例,SingleRequest是Request的子類
    return SingleRequest.obtain(
        context,
        glideContext,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListeners,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory(),
        callbackExecutor);
  }

小結(jié):構(gòu)建Request時庞瘸,除了構(gòu)建原圖的Request請求之外捧弃,還會判斷是否需要需要設置errorRequest和thumbnailRequest,然后協(xié)調(diào)后這個幾個請求之間的調(diào)用流程擦囊, 最后返回的是SingleRequest實例违霞。

2.4、requestManager.track(target, request) 執(zhí)行圖片請求
public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {
  
 
  private final RequestTracker requestTracker;
  //RequestTracker用于記錄所有的Target, 以及發(fā)送生命周期事件
  private final TargetTracker targetTracker = new TargetTracker();


  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);//添加到TargetTracker的集合中
    requestTracker.runRequest(request);執(zhí)行請求
  }

}

//用于跟蹤進行中請求瞬场、取消和重啟已完成或已失敗的請求
public class RequestTracker {

  //記錄所有的request
  private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
  //記錄等待執(zhí)行的request  
  private final List<Request> pendingRequests = new ArrayList<>();
  private boolean isPaused;

  public void runRequest(@NonNull Request request) {
    requests.add(request);
    //判斷是否在暫停狀態(tài)
    if (!isPaused) {
      request.begin();//調(diào)用SingleRequest的begin方法
    } else {
      request.clear();//調(diào)用SingleRequest的clear方法
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      
      //暫停狀態(tài)情況下买鸽,先記錄起等待執(zhí)行的request
      pendingRequests.add(request);
    }
  }

}

RequestTracker負責跟蹤進行中請求、取消和重啟已完成或已失敗的請求贯被。
requestManager.track(target, request)就是執(zhí)行圖片請求眼五,調(diào)用SingleRequest的begin方法

3、SingleRequest#begin()

public final class SingleRequest<R> implements Request,
    SizeReadyCallback,
    ResourceCallback,
    FactoryPools.Poolable {

  //Engine類主要負責啟動下載和管理緩存中的和活躍未回收的資源
  private Engine engine;
  private Resource<R> resource;
  private final StateVerifier stateVerifier = StateVerifier.newInstance();

  @Override
  public synchronized void begin() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();

    ...

    if (status == Status.RUNNING) {
      throw new IllegalArgumentException("Cannot restart a running request");
    }

    //如果已經(jīng)下載完成彤灶,直接回調(diào)onResourceReady看幼,代表加載成功
    if (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }

    //即未完成也不在運行中的請求,可以當做是新的請求幌陕,從頭開始
    status = Status.WAITING_FOR_SIZE;
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      // 如果使用了 override() API 為圖片指定了一個固定的寬高
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      // 沒有指定則調(diào)用 target.getSize()
      // target.getSize() 方法的內(nèi)部會根據(jù) ImageView 的
      // layout_width 和 layout_height 值做一系列的計算诵姜,來算出圖片應該的寬高
      // 計算完之后,它也會調(diào)用 onSizeReady() 方法
      target.getSize(this);
    }

    //如果是運行中苞轿,或者還在imageView控件的尺寸時
    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      //回調(diào)onLoadStarted的方法茅诱,把placeholder()中設置的圖片設置給view
      target.onLoadStarted(getPlaceholderDrawable());
    }

    ...
  }

SingleRequest的begin方法中逗物,會進行多個判斷,

  • status為COMPLETE就這直接回調(diào)onResourceReady方法瑟俭,
  • status為running就拋出異常翎卓,
  • status為WAITING_FOR_SIZE,判斷是否調(diào)用override方法設置指定寬高摆寄,如果指定好了寬高值失暴,直接調(diào)用onSizeReady方法;如果沒有微饥,調(diào)用target的getSize方法獲取控件的寬高逗扒,ViewTarget以及其子類實現(xiàn)了View 的 OnPreDrawListener接口,View 初始化完成后也會調(diào)用 SingleRequest的onSizeReady方法
3.1 onSizeReady()
  /*
   * begin方法中并不會直接發(fā)起請求欠橘,而是等待 ImageView 初始化完成矩肩;
   * 對于 ViewTarget 以及其子類來說,會注冊View 的 OnPreDrawListener 事件肃续,
   * 等待 View 初始化完成后就調(diào)用onSizeReady方法黍檩,才會開始加載圖片
   */
  @Override
  public synchronized void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();

    if (status != Status.WAITING_FOR_SIZE) {
      return;
    }
    status = Status.RUNNING;
    float sizeMultiplier = requestOptions.getSizeMultiplier();//獲取設置的縮放比例,計算圖片請求的寬高
    this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
    this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

    //調(diào)用engine的加載方法
    loadStatus =
        engine.load(...);

    ...
  }

}

onSizeReady方法主要就是engine的load方法始锚。

3.2 engine.load()

Engine類負責啟動下載和管理活動資源刽酱、緩存等等。

/**
  *Engine類主要負責啟動下載和管理緩存中的和活躍未回收的資源
  */
public class Engine implements EngineJobListener,
    MemoryCache.ResourceRemovedListener,
    EngineResource.ResourceListener {


  //ActiveResources內(nèi)部設置了HashMap集合瞧捌,保存EngineKey到EngineResource(圖片資源)的軟引用的映射
  private final ActiveResources activeResources;
  //內(nèi)部的HashMap集合保存EngineKey到EngineJob的映射
  private final Jobs jobs;
  

  /*
   * 構(gòu)建EngineJob和DecodeJob, 啟動DecodeJob
   */
  public synchronized <R> LoadStatus load(...) {
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;


    //buildKey方法是根據(jù)圖片請求的相關參數(shù)生成對應的key
    //EngineKey 用于緩存下載資源的key棵里,對應著一系列的具體的圖片請求參數(shù),不同的參數(shù)姐呐,相同的請求連接對應的key也不一樣
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);

    //從activeResources中的hashmap集合獲取EngineResource的弱引用殿怜,也就是獲取圖片資源引用
    //ActiveResources 是Glide的第一級緩存,表示當前正在活動中的資源
    //EngineResource 是Resource接口的實現(xiàn)類皮钠,Resource接口包裝了圖片資源的泛型稳捆,是圖片資源的包裝類
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      //EngineResource的引用,直接回調(diào)SingleRequest的onResourceReady方法
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return null;
    }

    //從LruCache緩存中獲取資源
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      //從緩存中獲取到資源麦轰,直接回調(diào)SingleRequest的onResourceReady方法
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return null;
    }

    // Jobs 內(nèi)部使用HashMap保存EngineKey到EngineJob的映射
    //EngineJob 負責管理下載乔夯,啟動DecodeJob
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      //保存cb和callbackExecutor,后面獲取到資源后款侵,回調(diào)cb的onResourceReady方法
      current.addCallback(cb, callbackExecutor);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }

    ///緩存中沒有末荐,則新建一個engineJob 
    EngineJob<R> engineJob =
        engineJobFactory.build(...);

    /* 
     * 新建一個DecodeJob
     * 負責從緩存或數(shù)據(jù)源中加載原始數(shù)據(jù)并通過解碼器轉(zhuǎn)換為相應的資源類型(Resource)。
     * DecodeJob 實現(xiàn)了 Runnable 接口新锈,由 EngineJob 將其運行在指定線程池中
     */
    DecodeJob<R> decodeJob =
        decodeJobFactory.build(...);

    //記錄key和engineJob的映射
    jobs.put(key, engineJob);
    
    engineJob.addCallback(cb, callbackExecutor);
    //啟動decodeJob
    engineJob.start(decodeJob);

    ...
    return new LoadStatus(cb, engineJob);
  }


}
  1. 根據(jù)圖片請求的相關參數(shù)構(gòu)建EngineKey甲脏。
  2. 從緩存中獲取EngineKey對應的EngineResource對象,如果EngineResource不為空,直接回調(diào)SingleRequest的onResouceReady接口块请,然后返回娜氏;
  3. 如果換成中沒有EngineKey對應的EngineResource對象,那么查看緩存中是否存在EngineKey對應的EngineJob對象:
    (1) 如果有緩存中存在對應的EngineJob對象墩新,說明對應的圖片正在加載中贸弥,添加一個新的ResourceCallback和callbackExecutor到EngineJob中,最后直接返回一個新的LoadStatus海渊。添加了回調(diào)接口后绵疲,圖片資源下載成功后,可以回調(diào)本次添加ResourceCallback的onResourceReady方法臣疑。
    (2) 如果緩存中沒有EngineJob盔憨,就新建一個EngineJob和DecodeJob,然后啟動DecodeJob任務讯沈。

4郁岩、engineJob.start(decodeJob)

EngineJob負責管理圖片請求回調(diào),以及圖片下載完成后執(zhí)行回調(diào)芙盘。
DecodeJob負責從磁盤緩存或數(shù)據(jù)源中加載原始數(shù)據(jù)并通過解碼器轉(zhuǎn)換為相應的資源類型驯用。

class EngineJob<R> implements DecodeJob.Callback<R>,
    Poolable {

  public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;

    //如果是要解碼磁盤的圖片資源脸秽,返回true, GlideExecutor為diskCacheExecutor儒老,否則調(diào)用getActiveSourceExecutor()
    //diskCacheExecutor為磁盤緩存加載線程池,不允許用于網(wǎng)絡操作
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }

  //如果要解密磁盤緩存中的資源返回true, 否則返回false
  boolean willDecodeFromCache() {
    Stage firstStage = getNextStage(Stage.INITIALIZE);
    return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
  }

  //返回三種類型的線程池
  //sourceUnlimitedExecutor : 圖片源網(wǎng)絡加載線程池记餐,沒有核心線程數(shù)驮樊, 線程數(shù)無限制
  //animationExecutor : 動畫加載線程池
  //sourceExecutor : 圖片源網(wǎng)絡加載線程池,有核心線程數(shù)片酝,線程數(shù)有限
  private GlideExecutor getActiveSourceExecutor() {
    return useUnlimitedSourceGeneratorPool
        ? sourceUnlimitedExecutor : (useAnimationPool ? animationExecutor : sourceExecutor);
  }

}

  1. GlideExecutor可能為diskCacheExecutor囚衔、sourceUnlimitedExecutor 、animationExecutor 雕沿、sourceExecutor其中一種练湿,把DecodeJob提交給線程池執(zhí)行。
  2. DecodeJob實現(xiàn)了Runnable接口审轮,EngineJob的start方法就是啟動decodeJob的線程肥哎,執(zhí)行decodeJob的run方法。
4.1 DecodeJob.run()
class DecodeJob<R> {

    @Override
    public void run() {
      ...      

      // Methods in the try statement can invalidate currentFetcher, so set a local variable here to
      // ensure that the fetcher is cleaned up either way.
      DataFetcher<?> localFetcher = currentFetcher;

      if (isCancelled) {
        //如果取消加載疾渣,調(diào)用notifyFailed()通知加載資源失敗
        notifyFailed();
        return;
      }
      runWrapped();

      ...
  }

  //decodeJob初始化時篡诽, runReason被設置為RunReason.INITIALIZE
  private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        //判斷要解碼的數(shù)據(jù)來源,構(gòu)建DataFetcherGenerator實例榴捡,執(zhí)行runGenerators方法
        stage = getNextStage(Stage.INITIALIZE);
        currentGenerator = getNextGenerator();
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }

  //getNextStage方法是要確定我們將要解碼的數(shù)據(jù)是來源于哪里的數(shù)據(jù)
  //通常判斷順序為INITIALIZE -> RESOURCE_CACHE(圖片轉(zhuǎn)換之后的磁盤緩存) -> DATA_CACHE(原圖的磁盤緩存) -> SOURCE(數(shù)據(jù)源)
  private Stage getNextStage(Stage current) {
    switch (current) {
      //INITIALIZE為Stage的初始狀態(tài)
      case INITIALIZE:
        // 如果decodeCachedResource() 為 true杈女, 表示嘗試解碼緩存中已經(jīng)被轉(zhuǎn)換的圖片
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        // 如果decodeCachedData() 為 true,表示嘗試解碼磁盤中緩存的原始圖片
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // 如果調(diào)用者選擇只用緩存中檢索資源,則跳過從數(shù)據(jù)源加載
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

  //根據(jù)Stage的枚舉值來獲取DataFetcherGenerator, 返回DataFetcherGenerator的子類
  private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        // 對應被轉(zhuǎn)換的圖片的緩存的 Generator
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
        // 對應原始圖片的緩存的 Generator
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
        // 對應圖片原始數(shù)據(jù)源的 Generator
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }

  
  private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    //這里判斷一下喘鸟,之后主要是執(zhí)行currentGenerator.startNext()方法執(zhí)行后續(xù)的邏輯
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();

      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    // We've run out of stages and generators, give up.
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }

  }

}

Stage是DecodeJob的內(nèi)部枚舉類芋绸,用于表示所要解碼數(shù)據(jù)的來源(內(nèi)存緩存, 磁盤緩存)躏筏,初始化狀態(tài)是INITIALIZE。

  1. 這里首先是要調(diào)用getNextStage()方法確定需要解碼的數(shù)據(jù)來源呈枉,是緩存的圖片還是原始圖片趁尼;
  2. 根據(jù)要解碼的數(shù)據(jù)來源,構(gòu)建對應的DataFetcherGenerator派生類(ResourceCacheGenerator猖辫, DataCacheGenerator等)酥泞;DataFetcherGenerator負責構(gòu)建DataFetcher實例, DataFetcher接口負責加載圖片數(shù)據(jù)啃憎。
  3. 啟動DataFetcherGenerator芝囤,調(diào)用DataFetcherGenerator的startNext()方法,構(gòu)建DataFetcher辛萍。
  4. 默認情況下悯姊,一開始會調(diào)用ResourceCacheGenerator的startNext()方法,獲取磁盤緩存的數(shù)據(jù)贩毕。
  5. 如果磁盤沒有緩存到對應的圖片數(shù)據(jù)悯许,要從數(shù)據(jù)源頭加載圖片時(比如從網(wǎng)絡加載圖片),會調(diào)用SourceGenerator的startNext()方法辉阶。

5先壕、 SourceGenerator#startNext()

對應從數(shù)據(jù)源頭加載數(shù)據(jù)的場景,負責構(gòu)建DataFetchers谆甜。

class SourceGenerator implements DataFetcherGenerator,
    DataFetcher.DataCallback<Object>,
    DataFetcherGenerator.FetcherReadyCallback {

  private final DecodeHelper<?> helper;
  private volatile ModelLoader.LoadData<?> loadData;

  @Override
  public boolean startNext() {
    ...

    sourceCacheGenerator = null;

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        //加載數(shù)據(jù)
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

}
  • ModelLoader負責將多種復雜的數(shù)據(jù)模式轉(zhuǎn)變成DataFetcher可以加載的的具體資源類型垃僚。
  • LoadData是ModelLoader的內(nèi)部類,保存了圖片對應的唯一Key规辱,備用key集合緩存谆棺,以及對應DataFetcher派生類。
  • DataFetcher是真正負責加載資源的類罕袋,通過不同的派生類實現(xiàn)來加載不同的數(shù)據(jù)改淑。

當我們加載網(wǎng)絡圖片源數(shù)據(jù)時,loadData.fetcher的實例是HttpUrlFetcher炫贤,即調(diào)用HttpUrlFetcher的loadData方法來加載網(wǎng)絡圖片源數(shù)據(jù)溅固。

5.1 HttpUrlFetcher#loadData()
 @Override
  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> callback) {

      ...
      //獲取圖片資源的輸入流
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
       //回調(diào)數(shù)據(jù)
      callback.onDataReady(result);
      ...
  }

  //通過HttpURLConnection加載網(wǎng)絡圖片,返回圖片輸入流
  private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
      Map<String, String> headers) throws IOException {
    ...
    
    urlConnection = connectionFactory.build(url);
    ...
    urlConnection.connect();
    // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
    stream = urlConnection.getInputStream();
    if (isCancelled) {
      return null;
    }
    final int statusCode = urlConnection.getResponseCode();
    if (isHttpOk(statusCode)) {
      return getStreamForSuccessfulRequest(urlConnection);
    }
    ...
  }

HttpUrlFetcher加載網(wǎng)絡圖片數(shù)據(jù)就是通過HttpURLConnection連接url兰珍,獲取圖片輸入流侍郭,然后通過回調(diào)接口把輸入流返回出去。callback.onDataReady()首先會回調(diào)SourceGenerator類的onDataReady()方法。

6亮元、SourceGenerator#onDataReady()

當數(shù)據(jù)獲取成功后猛计,需要執(zhí)行的就是緩存圖片和把圖片設置到圖片控件上了。

  //SourceGenerator#onDataReady()
  @Override
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;//把inputStream賦值給dataToCache 
      //cb是SourceGenerator構(gòu)造函數(shù)傳遞進來的爆捞,cb的實例是DecodeJob
      cb.reschedule();
    } 
    ...
  }

  //DecodeJob#reschedule
  @Override
  public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    //callback是DecodeJob的init方法傳遞進來的奉瘤,callback的實例是EngineJob
    callback.reschedule(this);
  }

  //EngineJob#reschedule()
  @Override
  public void reschedule(DecodeJob<?> job) {
    //再次把DecodeJob提交到線程池中執(zhí)行,回調(diào)DecodeJob的run方法煮甥。
    getActiveSourceExecutor().execute(job);
  }

數(shù)據(jù)下載成功后盗温,會經(jīng)過很多的回調(diào)方法,回調(diào)過程為SourceGenerator#onDataReady() --> DecodeJob#reschedule() -> EngineJob#reschedule() --> DecodeJob#run()成肘。

6.1 再次運行DecodeJob線程
  //DecodeJob#run
  @Override
  public void run() {
      ...
      runWrapped();
      ...
  }
  
  private void runWrapped() {
    switch (runReason) {
      ...
      case SWITCH_TO_SOURCE_SERVICE:
        //runGenerators會再次調(diào)用SourceGenerator的startNext方法
        runGenerators();
        break;
      ...
  }

  //SourceGenerator#startNext()
  @Override
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      //把數(shù)據(jù)緩存到磁盤中
      cacheData(data);
    }
    
     //執(zhí)行了cacheData方法時卖局,會把sourceCacheGenerator重新賦值為DataCacheGenerator。
    //執(zhí)行DataCacheGenerator的startNext方法
    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    ...
}

第二次運行DecodeJob線程双霍,會再次調(diào)用SourceCacheGenerator的startNext方法砚偶,因為在數(shù)據(jù)下載成功的時候,把inputSteam賦值給了dataToCache洒闸,所以startNext方法首先調(diào)用cacheData()方法對數(shù)據(jù)進行磁盤緩存染坯,然后調(diào)用DataCacheGenerator的startNext()方法。

6.2 DataCacheGenerator#startNext()
  @Override
  public boolean startNext() {
    ...
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
      loadData =
          modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
              helper.getOptions());
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        // 這里的 fetcher 為 ByteBufferFetcher丘逸,調(diào)用其 loadData() 的時候又會把自身傳遞過去
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

DataCacheGenerator類中单鹿, loadData.fetcher的實例是ByteBufferFetcher對象,loadData.fetcher.loadData就是調(diào)用ByteBufferFetcher的loadData方法鸣个,同時DataCacheGenerator把對象自身作為回調(diào)接口傳遞給ByteBufferFetcher羞反。

    //ByteBufferFetcher#loadData()
    @Override
    public void loadData(@NonNull Priority priority,
        @NonNull DataCallback<? super ByteBuffer> callback) {
      ByteBuffer result;
      try {
        //讀取緩存文件,獲取圖片文件的ByteBuffer數(shù)據(jù)
        result = ByteBufferUtil.fromFile(file);
      } catch (IOException e) {
        ...
        callback.onLoadFailed(e);
        return;
      }
      //回調(diào)到DataCacheGenerator的onDataReady方法
      callback.onDataReady(result);
    }

  //DataCacheGenerator#onDataReady()
  @Override
  public void onDataReady(Object data) {
    //cb對象是SourceGenerator囤萤,所以又回調(diào)到SourceGenerator的onDataFetcherReady方法
    cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
  }

    //SourceGenerator#onDataFetcherReady()
  @Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {
    //SourceGenerator的cb是DecodeJob對象,所以再回調(diào)到DecodeJob的onDataFetcherReady()方法
    cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
  }

ByteBufferFetcher的loadData方法就是讀取圖片的磁盤緩存轉(zhuǎn)換成ByteBuffer數(shù)據(jù)是趴,然后把數(shù)據(jù)回調(diào)給DecodeJob對象涛舍。

7、解析圖片數(shù)據(jù):DecodeJob#onDataFetcherReady()

  @Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {
    this.currentSourceKey = sourceKey;  //sourceKey是圖片的網(wǎng)絡url地址
    this.currentData = data;  //data是圖片數(shù)據(jù)唆途,
    this.currentFetcher = fetcher;  //ByteBufferFetcher
    this.currentDataSource = dataSource;  //圖片的數(shù)據(jù)來源富雅,為REMOTE,即遠端圖片數(shù)據(jù)源
    this.currentAttemptingKey = attemptedKey; //attemptedKey和sourceKey一樣
    if (Thread.currentThread() != currentThread) {
      ...
    } else {
      try {
        // 調(diào)用 decodeFromRetrievedData 解析圖片數(shù)據(jù)
        decodeFromRetrievedData();
      }
      ...
    }
  }

  //解析圖片數(shù)據(jù)
  private void decodeFromRetrievedData() {
    
    Resource<R> resource = null;
    try {
      //currentFetcher為ByteBufferFetcher肛搬,
      //currentData為圖片數(shù)據(jù)
      //currentDataSource為DATA_DISK_CACHE
      //解碼圖片數(shù)據(jù)没佑,轉(zhuǎn)換成Resource類型(LazyBitmapDrawableResource的實例)
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }

  //把圖片資源回調(diào)出去,然后根據(jù)options參數(shù)温赔,緩存轉(zhuǎn)換的圖片到磁盤中蛤奢。
  private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    if (resource instanceof Initializable) {
      //resource對象為LazyBitmapDrawableResource實例,是BitmapResource的包裝類
      //initialize方法是執(zhí)行BitmapResource的initialize方法,執(zhí)行bitmap.prepareToDraw();
      ((Initializable) resource).initialize();
    }

    Resource<R> result = resource;
    LockedResource<R> lockedResource = null;
    if (deferredEncodeManager.hasResourceToEncode()) {
      lockedResource = LockedResource.obtain(resource);
      result = lockedResource;
    }

    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      if (deferredEncodeManager.hasResourceToEncode()) {
        //根據(jù)options參數(shù)啤贩,緩存轉(zhuǎn)換的圖片到磁盤中
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }

    //編碼完成待秃,釋放資源
    onEncodeComplete();
  }

  //圖片資源獲取成功,回調(diào)出去
  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    //callback為EngineJob的實例對象痹屹,回調(diào)到EngineJob的onResourceReady方法
    callback.onResourceReady(resource, dataSource);
  }
  1. onDataFetcherReady首先就是把圖片數(shù)據(jù)解析成Resource圖片包裝類章郁。
  2. 把圖片資源回調(diào)給EngineJob對象,然后根據(jù)options參數(shù)志衍,緩存轉(zhuǎn)換的圖片到磁盤中暖庄。
7.1 EngineJob#onResourceReady()
//在執(zhí)行engine.load方法中, 代碼current.addCallback(cb, callbackExecutor)把SingleRequest設置給EngineJob對象
synchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) {
  cbs.add(cb, callbackExecutor);
}

  @Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    synchronized (this) {
      this.resource = resource;
      this.dataSource = dataSource;
    }
    notifyCallbacksOfResult();
  }

  
  void notifyCallbacksOfResult() {
    ResourceCallbacksAndExecutors copy;
    Key localKey;
    EngineResource<?> localResource;
    synchronized (this) {
      ...
      //把resource包裝成EngineResource
      engineResource = engineResourceFactory.build(resource, isCacheable);
      hasResource = true;
      copy = cbs.copy();//ResourceCallbacksAndExecutors
      localKey = key;
      localResource = engineResource;
    }

    //listener為Engine實例對象楼肪,再回調(diào)到Engine的onEngineJobComplete方法
    listener.onEngineJobComplete(this, localKey, localResource);

    for (final ResourceCallbackAndExecutor entry : copy) {
      //把cb回調(diào)傳遞給CallResourceReady雄驹,并執(zhí)行CallResourceReady線程任務。
      entry.executor.execute(new CallResourceReady(entry.cb));
    }

  }


private class CallResourceReady implements Runnable {
    private final ResourceCallback cb;
    CallResourceReady(ResourceCallback cb) {
      this.cb = cb;
    }
    @Override
    public void run() {
      synchronized (EngineJob.this) {
        if (cbs.contains(cb)) {
          ...
          callCallbackOnResourceReady(cb);
          ...
        }
      }
    }
  }

  
  synchronized void callCallbackOnResourceReady(ResourceCallback cb) {
    try {
      //cb為SingleRequest實例對象淹辞,回調(diào)到SingleRequest的onResourceReady方法中
      cb.onResourceReady(engineResource, dataSource);
    }
    ...
  }
  1. 先把Resource對象包裝成EngineResource医舆。
  2. listener.onEngineJobComplete()方法會回調(diào)到Engine#onEngineJobComplete()的方法,把EngineResource對象回調(diào)給Engine象缀。
  3. entry.executor.execute(new CallResourceReady(entry.cb))啟動CallResourceReady線程蔬将。cb是SingleRequest實例對象,回調(diào)到SingleRequest的onResourceReady方法中央星,并且把EngineResource資源對象傳遞給SingleRequest對象霞怀。
8、Engine#onEngineJobComplete()

對EngineResource執(zhí)行緩存操作莉给。

  @Override
  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    // A null resource indicates that the load failed, usually due to an exception.
    if (resource != null) {
      //Engine實現(xiàn)了ResourceListener的接口
      //記錄key和ResourceListener接口的實現(xiàn)類(Engine類實例對象)到EngineResource對象中
      //當EngineResource的release方法被執(zhí)行時毙石,會回調(diào)ResourceListener的onResourceReleased方法,即回調(diào)到Engine類的onResourceReleased方法中
      resource.setResourceListener(key, this);
      
      //把數(shù)據(jù)緩存到activeResources中颓遏,Glide的第一級緩存徐矩,軟引用緩存
      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }

    jobs.removeIfCurrent(key, engineJob);
  }

  //當EngineResource對象的release方法執(zhí)行時,回調(diào)到此方法
  @Override
  public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    //從第一級緩存中去掉EngineResource對象
    activeResources.deactivate(cacheKey);
    if (resource.isCacheable()) {
      //把EngineResource對象保存到cache叁幢,Glide的第二級緩存滤灯,
      //cache對象是LruResourceCache實例對象, LruCache的子類
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }
  • 先把EngineKey和Engine設置給EngineResource對象保存起來曼玩。當EngineResource對象被釋放時鳞骤,EngineResource的release方法會回調(diào)Engine的onResourceReleased方法,從一級緩存中刪除EngineResource黍判,然后保存到二級緩存中豫尽。
  • 然后把EngineResource保存到Glide的一級緩存activeResources中。
9顷帖、SingleRequest#onResourceReady()
 @Override
  public synchronized void onResourceReady(Resource<?> resource, DataSource dataSource) {
    ...
    Object received = resource.get();//從EngineResource中獲取BitmapDrawable對象
    ...
    //調(diào)用onResourceReady重載方法
    onResourceReady((Resource<R>) resource, (R) received, dataSource);
  }


  private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {

    ...
    status = Status.COMPLETE;//設置狀態(tài)為完成狀態(tài)
    ...
    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      ...
      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation =
            animationFactory.build(dataSource, isFirstResource);
        //target為DrawableImageViewTarget美旧, 
        //回調(diào)target的onResourceReady方法渤滞,最后會回調(diào)DrawableImageViewTarget的setResource方法
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }

    notifyLoadSuccess();
  }

  //BitmapImageViewTarget#setResource
  @Override
  protected void setResource(Bitmap resource) {
    view.setImageBitmap(resource);//給控件設置bitmap圖像
  }

SingleRequest的onResourceReady方法會回調(diào)用target的onResourceReady方法,由于target是DrawableImageViewTarget的實例陈症,最終會回調(diào)到BitmapImageViewTarget的setResource方法蔼水,給view設置bitmap資源。

總結(jié): into(imageView)的大體流程如下录肯。
1趴腋、首先構(gòu)建圖片控件的包裝類ViewTarget,實例為DrawableImageViewTarget或者BitmapImageViewTarget论咏。
2优炬、構(gòu)建request,獲取SingleRequest實例對象厅贪,并執(zhí)行reqest.begin方法開始請求蠢护。
3、構(gòu)建EngineJob和DecodeJob养涮,通過EngineJob啟動decodeJob的任務葵硕。
4、構(gòu)建DataFetcher實例贯吓,通過HttpUrlConnection請求圖片網(wǎng)絡url懈凹,獲取圖片輸入流,并把輸入流回調(diào)出去悄谐。
5介评、緩存圖片到磁盤中,繼續(xù)回調(diào)圖片流爬舰。
6们陆、解析圖片數(shù)據(jù),轉(zhuǎn)換成EngineResource圖片包裝類情屹。
7坪仇、緩存圖片到Glide的一級緩存中,并設置后EngineResource釋放時屁商,緩存圖片到二級緩存中烟很。
8、設置圖片資源到圖片控件中蜡镶。

into.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市恤筛,隨后出現(xiàn)的幾起案子官还,更是在濱河造成了極大的恐慌,老刑警劉巖毒坛,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件望伦,死亡現(xiàn)場離奇詭異林说,居然都是意外死亡,警方通過查閱死者的電腦和手機屯伞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門腿箩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人劣摇,你說我怎么就攤上這事珠移。” “怎么了末融?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵钧惧,是天一觀的道長。 經(jīng)常有香客問我勾习,道長浓瞪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任巧婶,我火速辦了婚禮乾颁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艺栈。我一直安慰自己英岭,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布眼滤。 她就那樣靜靜地躺著巴席,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诅需。 梳的紋絲不亂的頭發(fā)上漾唉,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音堰塌,去河邊找鬼赵刑。 笑死,一個胖子當著我的面吹牛场刑,可吹牛的內(nèi)容都是我干的般此。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼牵现,長吁一口氣:“原來是場噩夢啊……” “哼铐懊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瞎疼,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤科乎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后贼急,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茅茂,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡捏萍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了空闲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片令杈。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖碴倾,靈堂內(nèi)的尸體忽然破棺而出逗噩,到底是詐尸還是另有隱情,我是刑警寧澤影斑,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布给赞,位于F島的核電站,受9級特大地震影響矫户,放射性物質(zhì)發(fā)生泄漏片迅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一皆辽、第九天 我趴在偏房一處隱蔽的房頂上張望柑蛇。 院中可真熱鬧,春花似錦驱闷、人聲如沸耻台。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盆耽。三九已至,卻和暖如春扼菠,著一層夾襖步出監(jiān)牢的瞬間摄杂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工循榆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留析恢,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓秧饮,卻偏偏與公主長得像映挂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盗尸,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容