Glide 4.9 源碼分析(一) —— 一次完整加載流程

前言

若想把握 Glide 圖片加載的精髓, 首先要理清 Glide 圖片加載的一次流程

// 這里便是與 Glide 3+ 的不同
RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.loading);
// 它需要一個(gè)
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);

好的, 可以看到 Glide 的使用方式極為簡(jiǎn)單, 但往往越是簡(jiǎn)單的背后, 越是隱藏了復(fù)雜的實(shí)現(xiàn), 接下來我們就一步一步的分析 Glide 4.9 的一次加載流程

一. with

public class Glide implements ComponentCallbacks2 {
    
  public static RequestManager with(@NonNull Context context) {
    // 1. 調(diào)用了 getRetriever 獲取一個(gè) RequestManagerRetriever
    // 2. 調(diào)用了 RequestManagerRetriever.get 獲取一個(gè) RequestManager 描述一個(gè)圖片加載請(qǐng)求的管理者
    return getRetriever(context).get(context);
  }
    
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // 1.1 調(diào)用了 Glide.get 獲取 Glide 對(duì)象
    // 1.2 通過 Glide 對(duì)象獲取一個(gè) RequestManagerRetriever
    // 這個(gè) Retriever 用來獲取一個(gè) RequestManager 對(duì)象, 可以參考 Android Framework 源碼中的 SystemRetriever
    return Glide.get(context).getRequestManagerRetriever();
  }
}

好的, 可以看到 Glide.with 操作, 主要做了兩件事情

  • 通過 Glide.getRetriever 獲取一個(gè) RequestManagerRetriever 對(duì)象, 它描述為請(qǐng)求管理的獲取器
    • 調(diào)用 Glide.get 獲取 Glide 對(duì)象
    • 調(diào)用 Glide.getRequestManagerRetriever, 獲取 RequestManagerRetriever 對(duì)象
  • 調(diào)用 getRequestManagerRetriever.get 獲取一個(gè) RequestManager

接下來我們一步一步的看, 首先是獲取 RequestManagerRetriever

一) 獲取 Glide 對(duì)象

從上面的分析可只, RequestManagerRetriever 是通過 Glide.getRequestManagerRetriever 獲取到的, 因此需要先獲取 Glide 對(duì)象的實(shí)例, 因此我們先看看這個(gè) Glide 是如何構(gòu)造的

public class Glide implements ComponentCallbacks2 {
    
  private static volatile Glide glide;

  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }
    return glide;
  }
  
  private static volatile boolean isInitializing;
  
  private static void checkAndInitializeGlide(@NonNull Context context) {
    if (isInitializing) {
      // 拋出二次初始的異常
    }
    isInitializing = true;
    // 進(jìn)行初始化操作
    initializeGlide(context);
    isInitializing = false;
  }
  
  private static void initializeGlide(@NonNull Context context) {
    // 創(chuàng)建了一個(gè) GlideBuilder() 實(shí)例傳入 initializeGlide, 很顯然是為了初始化 Glide 對(duì)象
    // 接下來我們就看看它是如何初始化 Glide 的
    initializeGlide(context, new GlideBuilder());
  }

  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();
    // 1. 獲取 @GlideModule 注解驅(qū)動(dòng)生成的 GeneratedAppGlideModuleImpl 和 GeneratedAppGlideModuleFactory 類
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    ......
    // 2. 嘗試從注解生成的 annotationGeneratedModule 中獲取 RequestManager 的構(gòu)造工廠對(duì)象
    RequestManagerRetriever.RequestManagerFactory factory = annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    // 3. 向 Glide 的 Builder 中添加這個(gè)請(qǐng)求管理器的構(gòu)造工廠
    builder.setRequestManagerFactory(factory);
    ......
    // 4. 構(gòu)建 Glide 的實(shí)體對(duì)象
    Glide glide = builder.build(applicationContext);
    ......
    // 5. 向 Application 中注冊(cè)一個(gè)組件的回調(diào), 用于檢測(cè)系統(tǒng) Config 改變和內(nèi)存占用量低的信號(hào)
    applicationContext.registerComponentCallbacks(glide);
    // 保存在靜態(tài)的成員變量中
    Glide.glide = glide;
  }
}

從上的代碼可以看到 Glide 對(duì)象是進(jìn)程間單例的, 初次創(chuàng)建會(huì)調(diào)用 initializeGlide 方法, 其處理事務(wù)如下

  • 首先找尋 @GlideModule 注解生成類(這里省略了它的實(shí)現(xiàn))
  • 然后將一個(gè) RequestManagerFactory 添加到 GlideBuilder 內(nèi)部
  • 后便構(gòu)建了一個(gè) Glide 的對(duì)象

我們主要關(guān)注一下 Glide 對(duì)象創(chuàng)建的過程

public final class GlideBuilder {
  // 管理線程池的引擎
  private Engine engine;
  // 1. 線程池
  private GlideExecutor sourceExecutor;
  private GlideExecutor diskCacheExecutor;
  private GlideExecutor animationExecutor;
  // 2. 內(nèi)存的緩存策略
  private MemorySizeCalculator memorySizeCalculator;
  private MemoryCache memoryCache;
  // 3. 享元復(fù)用池
  private BitmapPool bitmapPool;
  private ArrayPool arrayPool;
  // 4. 磁盤緩存和請(qǐng)求構(gòu)建工廠
  private DiskCache.Factory diskCacheFactory;
  private RequestManagerFactory requestManagerFactory;
 
  @NonNull
  Glide build(@NonNull Context context) { 
    /*
      1. 線程池
     */
    // 1.1 網(wǎng)絡(luò)操作使用線程池
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }
    // 1.2 磁盤緩存使用的線程池
    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }
    // 1.3 執(zhí)行動(dòng)畫的線程池
    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }
    /*
     2. 內(nèi)存緩存
     */
    // 2.1 描述一個(gè)內(nèi)存的計(jì)算器, 智能加載圖片的大小, 判斷其需要的內(nèi)存空間
    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }
    // 2.2 LRU 資源內(nèi)存緩存
    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }
    // 2.3 LRU 的 Bitmap 復(fù)用池
    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }
    // 2.4 LRU 數(shù)組復(fù)用池
    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }
    
    /*
     3 磁盤緩存的工廠
     */
    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
    
    // 4. RequestManagerRetriever 對(duì)象
    RequestManagerRetriever requestManagerRetriever =  new RequestManagerRetriever(requestManagerFactory);
    
    // 5. 構(gòu)建了一個(gè)負(fù)責(zé)管理線程池與緩存的執(zhí)行引擎
    if (engine == null) {
      engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              GlideExecutor.newAnimationExecutor(),
              isActiveResourceRetentionAllowed);
    }
    
    ......
    // 6. 創(chuàng)建了 Glide 對(duì)象
    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptions.lock(),
        defaultTransitionOptions,
        defaultRequestListeners,
        ......);
  }
    
}

好的, 可以看到 Glide 對(duì)象的構(gòu)建過程異常的復(fù)雜, 筆者調(diào)整了部分的數(shù)據(jù), 它們的流程如下

  • 構(gòu)建線程池
    • 根據(jù)不同的任務(wù)特性, 構(gòu)建了不同的線程池
  • 構(gòu)建內(nèi)存緩存策略
    • 內(nèi)存計(jì)算器
    • LRU 資源內(nèi)存緩存
    • 數(shù)組對(duì)象復(fù)用池
    • Bitmap 復(fù)用池
  • 創(chuàng)建了一個(gè) RequestManagerRetriever 描述請(qǐng)求管理對(duì)象的獲取器
  • 構(gòu)建引擎類, 用于組織和調(diào)度線程池和緩存
  • 創(chuàng)建 Glide 對(duì)象

Glide 的創(chuàng)建流程如下

Glide 的創(chuàng)建

public class Glide implements ComponentCallbacks2 {
    
  private final Registry registry;

  Glide(......) {
    // 1 將 Builder 中的線程池, 緩存池等保存
    this.engine = engine;
    this.bitmapPool = bitmapPool;
    this.arrayPool = arrayPool;
    this.memoryCache = memoryCache;
    this.requestManagerRetriever = requestManagerRetriever;
    this.connectivityMonitorFactory = connectivityMonitorFactory;
    // 2. 使用 registry 注冊(cè) Glide 需要的 Encoder 與 Decoder
    DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
    bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
    final Resources resources = context.getResources();
    // 創(chuàng)建注冊(cè)器
    registry = new Registry();
    // 注冊(cè)圖片頭解析器
    registry.register(new DefaultImageHeaderParser());    
    ......
    // 3. 構(gòu)建一個(gè) Glide 的上下文, 方便在請(qǐng)求時(shí)獲取全局?jǐn)?shù)據(jù)
    glideContext =
        new GlideContext(
            context,
            arrayPool,
            registry,
            imageViewTargetFactory,
            defaultRequestOptions,
            defaultTransitionOptions,
            defaultRequestListeners,
            engine,
            isLoggingRequestOriginsEnabled,
            logLevel);
}

Glide 對(duì)象的創(chuàng)建如下

  • 將 GlideBuilder 中的數(shù)據(jù)導(dǎo)入
  • 構(gòu)建一個(gè) registry, 注冊(cè)了眾多的編解碼器
  • 構(gòu)建了上下文對(duì)象, 保存可能會(huì)用到的數(shù)據(jù)

二) 獲取 RequestManagerRetriever

在創(chuàng)建 GlideBuilder.build 中, 我們看到了它 new 了一個(gè) RequestManagerRetriever 對(duì)象并且傳遞到了 Glide 對(duì)象內(nèi)部, 于是通過 Glide.getRequestManagerRetriever 就可以很方便的獲取到 RequestManagerRetriever 這個(gè)對(duì)象了

接下來我們看看如何通過 RequestManagerRetriever.get() 獲取 RequestManager 對(duì)象

三) 獲取 RequestManager

public class RequestManagerRetriever implements Handler.Callback {

  public RequestManager get(@NonNull Context context) {
    if (context == null) {
      throw new IllegalArgumentException("You cannot start a load on a null Context");
    } else if (Util.isOnMainThread() && !(context instanceof Application)) {
      // 1. 若在主線程 并且 Context 不為 Application
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper) {
        // 不斷的查找其 BaseContext, 判斷是否能夠與 FragmentActivity/Activity 等目標(biāo)匹配
        return get(((ContextWrapper) context).getBaseContext());
      }
    } 
    // 2. 若不在 MainThread 或 context 為 Application 的類型, 則使用 ApplicationManager
    return getApplicationManager(context);
  }
}

可以看到 RequestManagerRetriever.get 方法會(huì)判斷 Context 的類型

  • 若在主線程并且不為 Application 類型的 Context 則找尋其依賴的 Activity
  • 若非主線程或?yàn)?Application 類型的 Context, 則使用 ApplicationManager

為什么要優(yōu)先找到 Activity 呢, 這么做是何用意呢?, 我們帶著問題去看看參數(shù)為 Activity 的 get 的重載方法

1. 獲取 Activity 的 RequestManager

public class RequestManagerRetriever implements Handler.Callback { 
    
  public RequestManager get(@NonNull Activity activity) {
    if (Util.isOnBackgroundThread()) {
      // 若非主線程, 直接獲取 Application 類型的 RequestManager
      return get(activity.getApplicationContext());
    } else {
      // 不可在 activity 銷毀時(shí)執(zhí)行加載
      assertNotDestroyed(activity);
      // 獲取其 FragmentManager
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(
          activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }
  
  private RequestManager fragmentGet(@NonNull Context context,
      @NonNull android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    // 1. 從 Activity 中獲取一個(gè) RequestManagerFragment, 用于監(jiān)管 Activity 的聲明周期
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
    // 2. 獲取 Fragment 中保存的當(dāng)前頁面的請(qǐng)求管理器 
    RequestManager requestManager = current.getRequestManager();
    // 3. 不存在則創(chuàng)建一個(gè)請(qǐng)求管理器保存在 RequestManagerFragment 中
    if (requestManager == null) {
      Glide glide = Glide.get(context);
      requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    // 返回這個(gè)請(qǐng)求管理器
    return requestManager;
  }
  
  // 描述一個(gè)即將被 FragmentManager 添加的 RequestManagerFragment 緩存
  final Map<android.app.FragmentManager, RequestManagerFragment> pendingRequestManagerFragments =
      new HashMap<>();

  private RequestManagerFragment getRequestManagerFragment(
      @NonNull final android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    // 2.1 嘗試從 FragmentManager 中獲取這個(gè) Fragment
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    // 2.2 不存在則添加一個(gè)
    if (current == null) {
      // 2.3 從 pendingRequestManagerFragments 緩存中獲取一個(gè)
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        // 2.3.1 創(chuàng)建并更新到緩存
        current = new RequestManagerFragment();
        ......
        // 2.3.2 添加到等待被添加的緩存中
        // 因?yàn)樘砑拥?FragmentManager 有延遲, 用這種方式防止同一時(shí)間創(chuàng)建了兩個(gè) RequestManagerFragment 對(duì)象添加到 Activity 中
        pendingRequestManagerFragments.put(fm, current);
        // 2.3.3 添加到 FragmentManager 中
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        // 2.3.4 添加到 FragmentManager 成功, 通過 Handler 移除這個(gè)緩存
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }  
}

好的, 可以看到 RequestManagerRetriever 的 get 方法主要是在 Activity 頁面中添加一個(gè) RequestManagerFragment 實(shí)例, 以便用于監(jiān)聽 Activity 的生命周期, 然后給這個(gè) Fragment 注入一個(gè) RequestManager, 其處理的細(xì)節(jié)代碼中也注釋的比較詳細(xì)

  • 其中有個(gè)非常引人注目的細(xì)節(jié), 考慮到將 FragmentManger 添加 Fragment 有延遲, 為了防止同一時(shí)間創(chuàng)建了兩個(gè) RequestManagerFragment 添加到 FragmentManager, 因此它使用了 pendingRequestManagerFragments 進(jìn)行緩存

2. 獲取 Application 的 RequestManager

public class RequestManagerRetriever implements Handler.Callback { 
  
  private volatile RequestManager applicationManager;
  private final RequestManagerFactory factory;
    
  private RequestManager getApplicationManager(@NonNull Context context) {
    // Either an application context or we're on a background thread.
    if (applicationManager == null) {
      synchronized (this) {
        if (applicationManager == null) {
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager = factory.build(glide, new ApplicationLifecycle(),
              new EmptyRequestManagerTreeNode(), context.getApplicationContext());
        }
      }
    }
    return applicationManager;
  }  
    
}

很簡(jiǎn)單構(gòu)建了一個(gè)單例的 RequestManager, 用于處理所有的 context 類型的請(qǐng)求

三) 回顧

Glide.with 方法主要是為 Context 構(gòu)建 RequestManager , 主要流程如下

  • 獲取進(jìn)程間單例的 Glide 對(duì)象
    • 線程池
    • 緩存池
    • 編解碼器
  • 獲取進(jìn)程間單例的 RequestManagerRetriever 對(duì)象
  • 通過 RequestManagerRetriever 創(chuàng)建當(dāng)前 context 的 RequestManager
    • 若可以綁定 Activity, 則為 Activity 添加一個(gè) RequestManagerFragment, 其內(nèi)部含有 ReqeustManager 對(duì)象, 以便后續(xù)直接根據(jù) Activity 的生命周期管控 Glide 請(qǐng)求的處理
    • 若非可綁定 Activity, 則獲取一個(gè)單例的 applicationManager 專門用于處理這類請(qǐng)求

接下來我們看看這個(gè) RequestManager.load 方法

二. load

我們使用最熟悉的加載網(wǎng)絡(luò)圖片來分析這個(gè) RequestManager.load 方法

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {
        
  public RequestBuilder<Drawable> load(@Nullable String string) {
    // 1. 調(diào)用 asDrawable 創(chuàng)建一個(gè)目標(biāo)為 Drawable 的圖片加載請(qǐng)求
    // 2. 調(diào)用 load 將要加載的資源傳入
    return asDrawable().load(string);
  }
  
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }
    
  public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }
          
}

好的, 可以看到 RequestManager.load 方法先調(diào)用了 asDrawable 構(gòu)建一個(gè) RequestBuilder, 描述一個(gè)目標(biāo)資源為 Drawable 的圖片加載請(qǐng)求

然后調(diào)用了 RequestBuilder.load 方法將加載的數(shù)據(jù)源傳入, 我們看看這個(gè) load 方法做了什么

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
        
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
  
  // 描述加載的數(shù)據(jù)源
  @Nullable private Object model;
  // 描述這個(gè)請(qǐng)求是否已經(jīng)添加了加載的數(shù)據(jù)源
  private boolean isModelSet;
  
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

}

好的, 可以看到走到這里, RequestBuilder 就構(gòu)建好了, 接下來就等待執(zhí)行這個(gè)請(qǐng)求了, 我們看看它的 RequestBuilder 的 into 方法

三. RequestBuilder.into

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
    
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    ......
    // 1. 根據(jù) view 的 scaleType 重構(gòu) RequestOptions
    BaseRequestOptions<?> requestOptions = this;// RequestBuilder 直接繼承了 BaseRequestOptions
    if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) { 
      switch (view.getScaleType()) {
        case CENTER_CROP:
          // 1.1 克隆原 RequestOptions, 配置一個(gè) CenterCrop 的縮放選項(xiàng)
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        ......
      }
    }
    // 3. 調(diào)用 into 方法, 創(chuàng)建并且執(zhí)行請(qǐng)求
    return into(
        // 2. 將 View 封裝成 ViewTarget
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

}

可以看到 into 方法中主要執(zhí)行了如下的步驟

  • 根據(jù) ImageView 的 ScaleType 來配置 Options 選項(xiàng)
  • 將 View 封裝成 ViewTarget
  • 調(diào)用了重載方法 into 執(zhí)行后續(xù)構(gòu)建請(qǐng)求操作

我們以 CenterCrop 舉例, 看看它如何配置縮放效果

一) 配置 Options

public abstract class BaseRequestOptions<T extends BaseRequestOptions<T>> implements Cloneable { 
    
  public T optionalCenterCrop() {
    // 1. 調(diào)用了 optionalTransform
    // DownsampleStrategy 描述降采樣壓縮的策略
    // CenterCrop 描述圖像變化方式
    return optionalTransform(DownsampleStrategy.CENTER_OUTSIDE, new CenterCrop());
  }
  
  final T optionalTransform(@NonNull DownsampleStrategy downsampleStrategy,
      @NonNull Transformation<Bitmap> transformation) {
    ......
    // 2. 將降采樣壓縮策略添加到 options 中
    downsample(downsampleStrategy);
    // 3. 將圖像變化方式添加到 transformations 中
    return transform(transformation, /*isRequired=*/ false);
  }
  
  public T downsample(@NonNull DownsampleStrategy strategy) {
    // 2.1 調(diào)用了 set, 將降采樣策略保存到 options 中
    return set(DownsampleStrategy.OPTION, Preconditions.checkNotNull(strategy));
  }
  
  private Options options = new Options();
 
  public <Y> T set(@NonNull Option<Y> option, @NonNull Y value) {
    ...
    // 2.2 添加到 options 緩存中
    options.set(option, value);
    return selfOrThrowIfLocked();
  }
    
  T transform(@NonNull Transformation<Bitmap> transformation, boolean isRequired) {
    // 3.1 調(diào)用了 transform 的重載方法, 將這個(gè)圖像變化的方式作用到多種資源類型上
    DrawableTransformation drawableTransformation = new DrawableTransformation(transformation, isRequired);
    transform(Bitmap.class, transformation, isRequired);// Bitmap 類型的資源
    transform(Drawable.class, drawableTransformation, isRequired);// Drawable類型的
    ......
    return selfOrThrowIfLocked();
  }
  
  private Map<Class<?>, Transformation<?>> transformations = new CachedHashCodeArrayMap<>();

  <Y> T transform(@NonNull Class<Y> resourceClass, @NonNull Transformation<Y> transformation, boolean isRequired) {
    // 3.2 添加到了 transformations 緩存中
    transformations.put(resourceClass, transformation);
    return selfOrThrowIfLocked();
  }
}

好的, 可以看到配置縮放選項(xiàng)的操作除了添加了圖像變化操作, 還設(shè)定了采樣方式, 分別保存在 transformations 和 options 中

我們知道 GlideContext 是在 Glide 對(duì)象構(gòu)造時(shí)一并創(chuàng)建的, 它是 Context 的裝飾者對(duì)象, 在 Application 類型的 Context 中, 添加了 Glide 相關(guān)的數(shù)據(jù), 我們先看看它是如何構(gòu)建 ViewTarget 的

二) 構(gòu)建 ViewTarget

public class GlideContext extends ContextWrapper {
  
  private final ImageViewTargetFactory imageViewTargetFactory;

  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    // 調(diào)用工廠類來創(chuàng)建一個(gè) imageView 的 ViewTarget
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
    
}

public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) {
    // 根據(jù)目標(biāo)編碼的類型來創(chuàng)建不同的 ViewTarget 對(duì)象, 因?yàn)槲覀儧]有 asBitmap, 因此這里為 Drawable
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
      ......
    }
  }
} 

好的可以看到 GlideContext 中通過工廠類創(chuàng)建了 ImageView 的 ViewTarget 的, 它描述的是圖像處理結(jié)束之后, 最終要作用到的 View 目標(biāo)

三) into 重載方式

構(gòu)建好了 ViewTarge, 接下來就可以分析重載的 into 方法了, 看看它是如何構(gòu)建請(qǐng)求的

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
  
  private <Y extends Target<TranscodeType>> Y into(@NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,  Executor callbackExecutor) {
    ......
    // 1. 調(diào)用 buildRequest 構(gòu)建了一個(gè) Glide 請(qǐng)求
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    ......// 處理這個(gè) ViewTarget 之前的請(qǐng)求與新請(qǐng)求的沖突
    // 2. 給 ViewTarget 綁定這個(gè) Glide 請(qǐng)求
    target.setRequest(request);
    // 3. 調(diào)用了請(qǐng)求 RequestManager.track 方法分發(fā)請(qǐng)求
    requestManager.track(target, request);
    return target;
  }
  
}

可以看到 into 的重載方法主要執(zhí)行了如下的步驟

  • 將相關(guān)參數(shù)傳入構(gòu)建 Request 請(qǐng)求
  • 為 ViewTarget 綁定該請(qǐng)求
  • 調(diào)用 RequestManger.track 分發(fā)該請(qǐng)求

四) 回顧

RequestBuilder.into 主要做了如下的事情

  • 根據(jù) ImageView 構(gòu)建采樣壓縮和圖像變化的策略保存在 Options 和 Transform 中
  • 構(gòu)建 ViewTarget, 描述這個(gè)請(qǐng)求要作用的 View 對(duì)象
  • 調(diào)用 into 重載方法
    • 構(gòu)建 Request 對(duì)象
    • 為 ViewTarget 綁定 Request
    • 交由 RequestManger 執(zhí)行該請(qǐng)求

到這里我們的 Glide.with().load().into 就走完了, 最終會(huì)構(gòu)建一個(gè) Request 交由 RequestManger 分發(fā)執(zhí)行

下面我們看看 RequestManager 是如何執(zhí)行 Request 的

四. RequestManager 執(zhí)行 Request 請(qǐng)求

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {
  
  private final RequestTracker requestTracker;
    
  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    ......
    // 1. 執(zhí)行請(qǐng)求
    requestTracker.runRequest(request);
  }
            
}

public class RequestTracker {
    
  private final Set<Request> requests =
      Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
  private final List<Request> pendingRequests = new ArrayList<>();
  
  public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {
      // 2. 調(diào)用 request.begin 執(zhí)行任務(wù)
      request.begin();
    } else {
      ......
    }
  }
  
}

public final class SingleRequest<R> implements Request,
    SizeReadyCallback,
    ResourceCallback,
    FactoryPools.Poolable {
  
  public synchronized void begin() {
    ......
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      // 3. 表示尺寸準(zhǔn)備好了
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      ......
    }
    ......
  }
  
  private Engine engine;
  private int width;
  private int height;
  
  public synchronized void onSizeReady(int width, int height) {
    ......
    // 4. 調(diào)用了 Engine.load 方法構(gòu)建任務(wù)
    loadStatus = engine.load(......);
    ......
  }
          
}

好的, 可以看到最終調(diào)用到了 onSizeReady 去構(gòu)建可執(zhí)行任務(wù), 接下來我們就分析這一過程

一) 任務(wù)的構(gòu)建

public class Engine implements EngineJobListener,
    MemoryCache.ResourceRemovedListener,
    EngineResource.ResourceListener {
  
  private final Jobs jobs;

  public synchronized <R> LoadStatus load(...) {
    // 1. 根據(jù)傳入的參數(shù), 構(gòu)建這個(gè)請(qǐng)求的 key
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
    // 2. 從緩存中查找 key 對(duì)應(yīng)的資源
    // 2.1 嘗試從 ActiveResources 緩存中查找這個(gè) key 的緩存
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      // 若緩存存在, 則直接回調(diào) onResourceReady 處理后續(xù)操作
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      .......
      return null;
    }
    // 2.2 嘗試從 LruResourceCache 中找尋這個(gè)資源 
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      // 回調(diào) onResourceReady 處理后續(xù)操作
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      return null;
    }
    // 3. 從緩存中查找 key 對(duì)應(yīng)的任務(wù)
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      // 3.1 走到這里說明這個(gè)任務(wù)已經(jīng)正在執(zhí)行了, 無需再次構(gòu)建執(zhí)行
      current.addCallback(cb, callbackExecutor);
      ......
      // 返回加載狀態(tài)即可
      return new LoadStatus(cb, current);
    }
    // 3.2 走到這里, 說明是一個(gè)新的任務(wù)
    // 3.2.1 則構(gòu)建一個(gè)新的引擎任務(wù)
    EngineJob<R> engineJob = engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);
    // 3.2.2 構(gòu)建解碼任務(wù)
    DecodeJob<R> decodeJob = decodeJobFactory.build(......, engineJob);
    // 3.2.3 添加到任務(wù)緩存
    jobs.put(key, engineJob);
    ......
    // 3.2.4 執(zhí)行任務(wù)
    engineJob.start(decodeJob);
    ......
  }
    
}

好的, 可以看到 Engine.load 中的事情非常的重要

  • 構(gòu)建這個(gè)請(qǐng)求的 key
  • 從內(nèi)存緩存中查找 key 對(duì)應(yīng)的資源, 若存在直接回 onResourceReady 表示資源準(zhǔn)備好了
    • 從 ActiveResources 緩存中查找
    • 從 LruResourceCache 緩存中查找
  • 從緩存中查找 key 對(duì)應(yīng)的任務(wù)
    • 若存在則說明無需再次獲取資源
    • 構(gòu)建任務(wù)
      • 構(gòu)建引擎任務(wù) EngineJob
      • 引擎的任務(wù)為解碼任務(wù) DecodeJob
      • 將任務(wù)添加到緩存, 防止多次構(gòu)建
      • 執(zhí)行 EngineJob

好的, 可以看到內(nèi)存緩存的處理是在 Engine 中進(jìn)行的, 若兩個(gè)內(nèi)存緩存都沒有命中, 則會(huì)構(gòu)建任務(wù)并執(zhí)行, 接下來我們看看任務(wù)的執(zhí)行過程

二) EngineJob 的執(zhí)行

class EngineJob<R> implements DecodeJob.Callback<R>,
    Poolable {
    
  private final GlideExecutor diskCacheExecutor;  
  private DecodeJob<R> decodeJob;

  public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    // 獲取線程池
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor : getActiveSourceExecutor();
    // 執(zhí)行任務(wù)
    executor.execute(decodeJob);
  }
  
}

簡(jiǎn)單的交由了線程池執(zhí)行這個(gè) DecodeJob

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
    
  @Override      
  public void run() {
     try {
      ......
      // 調(diào)用了 runWrapped
      runWrapped();
    } catch (CallbackException e) {
      ......
    }
  }        
  
  private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        // 1. 獲取合適的任務(wù)場(chǎng)景
        stage = getNextStage(Stage.INITIALIZE);
        // 2. 獲取這個(gè)場(chǎng)景的執(zhí)行者
        currentGenerator = getNextGenerator();
        // 3. 執(zhí)行者執(zhí)行任務(wù)
        runGenerators();
        break;
      ......
    }
  }
  
  private Stage getNextStage(Stage current) {
    switch (current) {
      // 1.1 判斷是否允許讀取磁盤資源緩存, 若允許則返回 Stage.RESOURCE_CACHE, 不允許則繼續(xù)找尋合適的場(chǎng)景
      case INITIALIZE:
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      // 1.2 判斷是否允許從磁盤讀取源數(shù)據(jù)緩存, 若允許則返回 Stage.DATA_CACHE, 不允許則繼續(xù)查找
      case RESOURCE_CACHE:
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      // 1.3 若只能允許從緩存中獲取數(shù)據(jù), 則直接 FINISH, 否則返回 Stage.SOURCE, 意為加載一個(gè)新的資源
      case DATA_CACHE:
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }
  
  private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        // 2.1 資源磁盤緩存的執(zhí)行者
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
        // 2.2 源數(shù)據(jù)磁盤緩存的執(zhí)行者
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
        // 2.3 無緩存, 獲取數(shù)據(jù)的源的執(zhí)行者
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }
  
  private void runGenerators() {
    ......
    boolean isStarted = false;
    while (!isCancelled && currentGenerator != null
        // 3.1 調(diào)用 currentGenerator.startNext() 執(zhí)行當(dāng)前場(chǎng)景的任務(wù)
        && !(isStarted = currentGenerator.startNext())) {
      // 3.2 若執(zhí)行失敗, 則獲取下一個(gè)場(chǎng)景繼續(xù)執(zhí)行
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();
      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    ......
  }
  
}

DecodeJob 的 runWrapper 方法主要做了如下的事務(wù)

  • 通過 getNextStage 獲取 Decode 場(chǎng)景
  • 構(gòu)建當(dāng)前場(chǎng)景的處理器 DataFetcherGenerator
  • 調(diào)用 runGenerators 處理任務(wù)
    • 優(yōu)先從 Resource 的磁盤緩存中取數(shù)據(jù)
    • 次優(yōu)先從 Data 的磁盤緩存中取數(shù)據(jù)
    • 最終方案重新獲取源數(shù)據(jù)
場(chǎng)景 場(chǎng)景描述 場(chǎng)景執(zhí)行器
Stage.RESOURCE_CACHE 從磁盤中緩存的資源中獲取數(shù)據(jù) ResourceCacheGenerator
Stage.DATA_CACHE 從磁盤中緩存的源數(shù)據(jù)中獲取數(shù)據(jù) DataCacheGenerator
Stage.SOURCE 重新請(qǐng)求數(shù)據(jù) SourceGenerator

關(guān)于 Resource 和 Data 磁盤緩存感興趣可以自行分析, 我們這里主要看看 SourceGenerator 獲取方案源數(shù)據(jù)方案

1. SourceGenerator 獲取數(shù)據(jù)流

class SourceGenerator implements DataFetcherGenerator,
    DataFetcher.DataCallback<Object>,
    DataFetcherGenerator.FetcherReadyCallback {
  
  private final DecodeHelper<?> helper;
  
  public boolean startNext() {
    ......
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      // 1. 從 DecodeHelper 的數(shù)據(jù)加載集合中, 獲取一個(gè)數(shù)據(jù)加載器
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        // 2. 使用加載器中 fetcher 執(zhí)行數(shù)據(jù)加載
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }
  
}

好的, SourceGenerator 主要有兩步

  • 調(diào)用 DecodeHelper.getLoadData 獲取當(dāng)前請(qǐng)求的數(shù)據(jù)加載器
  • 調(diào)用加載器中的 fetcher.loadData 真正的執(zhí)行數(shù)據(jù)加載
1) 獲取數(shù)據(jù)加載器 LoadData
final class DecodeHelper<Transcode> {
    
  private final List<LoadData<?>> loadData = new ArrayList<>();
  private GlideContext glideContext;
  private Object model;
  private boolean isLoadDataSet;    
  
  List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      // 1. 從 Glide 注冊(cè)的 register 中獲取請(qǐng)求 model 加載器
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
      // 遍歷每一個(gè) modelLoaders 
      for (int i = 0, size = modelLoaders.size(); i < size; i++) {
        // 2. 通過 modelLoaders 構(gòu)建 loadData
        ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
        LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
        if (current != null) {
          // 添加到緩存
          loadData.add(current);
        }
      }
    }
    return loadData;
  }  
    
}

它會(huì)找到一個(gè) ModelLoader 的實(shí)現(xiàn)類, 通過這個(gè)實(shí)現(xiàn)類的 handles 方法, 判斷是否可以加載這個(gè) model

這里我們的 model 以網(wǎng)絡(luò)的 URL 資源舉例, 它的實(shí)現(xiàn)類為 HttpGlideUrlLoader 我們看看它如何構(gòu)建一個(gè) LoadData 對(duì)象的

public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
  
  @Nullable private final ModelCache<GlideUrl, GlideUrl> modelCache;
  
  @Override
  public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
      @NonNull Options options) {  
    GlideUrl url = model;
    .....
    int timeout = options.get(TIMEOUT);
    // 創(chuàng)建了一個(gè) LoadData 對(duì)象, 并且實(shí)例化了一個(gè) HttpUrlFetcher 給它
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }
    
}

好的, 可以看到對(duì)于 URL 的加載, 其 fetcher 為一個(gè) HttpUrlFetcher 的實(shí)例, 接下來我們看看數(shù)據(jù)加載的流程

2) 執(zhí)行數(shù)據(jù)加載

獲取到了數(shù)據(jù)加載器之后, SourceGenerator 的 startNext 中便會(huì)調(diào)用其 fetcher 的 loadData 執(zhí)行數(shù)據(jù)的加載了, 我們結(jié)下來便分析一下這個(gè)過程

public class HttpUrlFetcher implements DataFetcher<InputStream> {
    
  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      // 獲取網(wǎng)絡(luò)圖片, 內(nèi)部使用了 HttpConnection 實(shí)現(xiàn), 僅僅做了重定向的處理
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      // 將 inputStream 回調(diào)出去
      callback.onDataReady(result);
    } catch (IOException e) {
      ......
      callback.onLoadFailed(e);
    } finally {
      ......
    }
  }
    
}

好的, 數(shù)據(jù)加載的過程也是很簡(jiǎn)單的, HttpUrlFetcher 它使用了 HttpConnection 發(fā)起了網(wǎng)絡(luò)請(qǐng)求, 獲取了數(shù)據(jù)流, 至此數(shù)據(jù)資源的獲取就已經(jīng)完成了, 后面要做的便是最重要的數(shù)據(jù)處理了, 它通過回調(diào)的方式將 InputStream 扔了出去

最終會(huì)回溯到 DecodeJob 的 onDataFetcherReady 處理這個(gè) InputStream

2. DecodeJob 解碼數(shù)據(jù)流

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {

  private Key currentSourceKey;
  private Object currentData;
  private DataSource currentDataSource;
  private DataFetcher<?> currentFetcher;
  
  @Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {
    this.currentSourceKey = sourceKey;  // 保存數(shù)據(jù)的 key
    this.currentData = data;            // 保存數(shù)據(jù)實(shí)體
    this.currentFetcher = fetcher;      // 保存數(shù)據(jù)的獲取器
    this.currentDataSource = dataSource;// 數(shù)據(jù)來源: url 為 REMOTE 類型的枚舉, 表示從遠(yuǎn)程獲取
    ......
    if (Thread.currentThread() != currentThread) {
      ......
    } else {
      try {
        // 調(diào)用 decodeFromRetrievedData 解析獲取的數(shù)據(jù)
        decodeFromRetrievedData();
      } finally {
        ......
      }
    }
  }
  
  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      // 1. 調(diào)用了 decodeFromData 獲取資源
      resource = decodeFromData(/*HttpUrlFetcher*/currentFetcher, /*InputStream*/currentData,/*REMOTE*/ currentDataSource);
    } catch (GlideException e) {
      ......
    }
    if (resource != null) {
      // 2. 通知外界資源獲取成功了
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      ......
    }
  }
  
}

DecodeJob 解碼數(shù)據(jù)流的操作如下

  • 首先要將 InputStream 解碼為 Resource 類型(并非 Android Resource)
  • Resource 的緩存與展示

我們先看看它是如何將數(shù)據(jù)解析成 Resource

1) 解碼 InputStream
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
  
  private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
      DataSource dataSource) throws GlideException {
    try {
      ......
      // 調(diào)用了 decodeFromFetcher
      Resource<R> result = decodeFromFetcher(data, dataSource);
      ......
      return result;
    } finally {
    }
  }
  
  private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    // 1. 獲取當(dāng)前數(shù)據(jù)類的解析器 LoadPath
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    // 2. 通過解析器來解析來解析數(shù)據(jù)
    return runLoadPath(data, dataSource, path);
  }
  
  private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
      LoadPath<Data, ResourceType, R> path) throws GlideException {
    Options options = getOptionsWithHardwareConfig(dataSource);
    // 2.1 根據(jù)數(shù)據(jù)類型獲取一個(gè)數(shù)據(jù)重造器, 獲取的數(shù)據(jù)為 InputStream, 因此它是一個(gè) InputStreamRewinder 的實(shí)例
    DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
    try {
      // 2.2 將解析資源的任務(wù)轉(zhuǎn)移到了 LoadPath.load 方法中
      return path.load( rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
  }        
}

可以看到為了解析數(shù)據(jù), 首先構(gòu)建了一個(gè) LoadPath, 然后創(chuàng)建了一個(gè) InputStreamRewinder 類型的 DataRewinder, 最終將數(shù)據(jù)解析的操作到了 LoadPath.load 方法中

接下來看看這個(gè)LoadPath.load 做了哪些處理

public class LoadPath<Data, ResourceType, Transcode> {
 
 public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,
      int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
    ......
    try {
      return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
    } finally {
      ......
    }
  }

  private final List<? extends DecodePath<Data, ResourceType, Transcode>> decodePaths;
  
  private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
      @NonNull Options options, int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
      List<Throwable> exceptions) throws GlideException {
    Resource<Transcode> result = null;
    // 遍歷內(nèi)部存儲(chǔ)的 DecodePath 集合, 通過他們來解析數(shù)據(jù)
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
      try {
        // 調(diào)用 DecodePath.decode 真正進(jìn)行數(shù)據(jù)的解析
        result = path.decode(rewinder, width, height, options, decodeCallback);
      } catch (GlideException e) {
        ......
      }
      ......
    }
    return result;
  }
      
}

public class DecodePath<DataType, ResourceType, Transcode> {
    
  public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
      @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
    // 1. 調(diào)用 decodeResource 將源數(shù)據(jù)解析成中間資源
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    // 2. 調(diào)用 DecodeCallback.onResourceDecoded 處理中間資源
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    // 3. 調(diào)用 ResourceTranscoder.transcode 將中間資源轉(zhuǎn)為目標(biāo)資源
    return transcoder.transcode(transformed, options);
  }

可以看到 DecodePath.decode 操作一共有三種 resource

  • 調(diào)用 decodeResource 將源數(shù)據(jù)解析成資源
    • 即獲取 Bitmap 的過程
  • 調(diào)用 DecodeCallback.onResourceDecoded 處理資源
    • 對(duì) Bitmap 進(jìn)行 Transform 操作
  • 調(diào)用 ResourceTranscoder.transcode 將資源轉(zhuǎn)為目標(biāo)資源
    • 將 Bitmap 轉(zhuǎn)為目標(biāo)類型

我們先看看獲取 Bitmap 的操作

Step1 獲取 Bitmap
  private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
      int height, @NonNull Options options) throws GlideException {
    try {
      // 調(diào)用了 decodeResourceWithList
      return decodeResourceWithList(rewinder, width, height, options, exceptions);
    } finally {
      ......
    }
  }

  @NonNull
  private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
      int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
    Resource<ResourceType> result = null;
    for (int i = 0, size = decoders.size(); i < size; i++) {
      ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
      try {
        DataType data = rewinder.rewindAndGet();
        if (decoder.handles(data, options)) {
          data = rewinder.rewindAndGet();
          // 調(diào)用 ResourceDecoder.decode 解析源數(shù)據(jù)
          result = decoder.decode(data, width, height, options);
        }
      } 
      ......
      if (result != null) {
        break;
      }
    }
    return result;
  }
    
}

因?yàn)楸敬瘟鞒痰脑磾?shù)據(jù)為 InputStream 因此它的解析器為 StreamBitmapDecoder

public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {

  private final Downsampler downsampler;
  
  public Resource<Bitmap> decode(@NonNull InputStream source, int width, int height,
      @NonNull Options options)
      throws IOException {
    ......
    try {
      // 根據(jù)請(qǐng)求配置的數(shù)據(jù), 對(duì)數(shù)據(jù)流進(jìn)行采樣壓縮, 獲取到一個(gè) Resource<Bitmap>
      return downsampler.decode(invalidatingStream, width, height, options, callbacks);
    } finally {
      ......
    }
  }
    
}

可以看到它內(nèi)部通過 Downsampler.decode 方法對(duì)數(shù)據(jù)流進(jìn)行采樣壓縮, 來獲取這個(gè)流的 Bitmap

  • 這個(gè)采樣的策略就是我們?cè)跇?gòu)建 Request 時(shí)傳入的, 其采樣壓縮的細(xì)節(jié), 并不是我們本次關(guān)注的重點(diǎn)

我們看看獲取到了 Resource<Bitmap> 之后, 如何處理這個(gè)資源

Step2 Transform Bitmap

可以看到, 當(dāng)我們將源數(shù)據(jù)解析成對(duì)應(yīng)的資源之后, 便會(huì)調(diào)用 DecodeCallback.onResourceDecoded 處理資源, 我們看看它的處理過程

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
    
  private final class DecodeCallback<Z> implements DecodePath.DecodeCallback<Z> {

    @Override
    public Resource<Z> onResourceDecoded(@NonNull Resource<Z> decoded) {
      // 調(diào)用了外部類的 onResourceDecoded 方法
      return DecodeJob.this.onResourceDecoded(dataSource, decoded);
    }
    
  }
  
  private final DeferredEncodeManager<?> deferredEncodeManager = new DeferredEncodeManager<>();

  <Z> Resource<Z> onResourceDecoded(DataSource dataSource,
      @NonNull Resource<Z> decoded) {
      
    // 1. 調(diào)用 Transformation.transformation 對(duì) Bitmap 進(jìn)行變化操作
    Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
    Transformation<Z> appliedTransformation = null;
    Resource<Z> transformed = decoded;
    
    if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
      appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
      transformed = appliedTransformation.transform(glideContext, decoded, width, height);
    }
    ......
    
    // 2. 構(gòu)建磁盤緩存策略
    final EncodeStrategy encodeStrategy;
    final ResourceEncoder<Z> encoder;
    if (decodeHelper.isResourceEncoderAvailable(transformed)) {
      encoder = decodeHelper.getResultEncoder(transformed);
      encodeStrategy = encoder.getEncodeStrategy(options);
    } else {
      encoder = null;
      encodeStrategy = EncodeStrategy.NONE;
    }
    Resource<Z> result = transformed;
    boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
    if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
        encodeStrategy)) {
      ......
      final Key key;
      switch (encodeStrategy) {
        case SOURCE:
          // 源 Bitmap 即 Source 緩存
          key = new DataCacheKey(currentSourceKey, signature);
          break;
        case TRANSFORMED:
          // 變化后的 Bitmap 即 Resource 緩存
          key =
              new ResourceCacheKey(......);
          break;
        default:
          throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
      }
      // 5. 初始化編碼管理者, 用于提交內(nèi)存緩存
      LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
      deferredEncodeManager.init(key, encoder, lockedResult);
      result = lockedResult;
    }
    // 返回 transform 之后的 bitmap
    return result;
  }
        
}

可以看到 onResourceDecoded 中, 主要是對(duì)中間資源做了如下的操作

  • 調(diào)用 Transformation.transformation 對(duì) Bitmap 進(jìn)行變化操作
    • 將資源轉(zhuǎn)為目標(biāo)效果, 如在構(gòu)建 request 時(shí), 設(shè)置的 CenterCrop
  • 構(gòu)建磁盤緩存的 key
    • 原始 Bitmap 即: Source 緩存
    • Tranform 之后的為 Resource 緩存

好的, 這個(gè)方法執(zhí)行結(jié)束之后, 這個(gè)資源就與我們期望的效果一致了, 接下來只需要將它轉(zhuǎn)為目標(biāo)格式就可以展示了

Step3 將 Bitmap 轉(zhuǎn)為目標(biāo)類型

目標(biāo)數(shù)據(jù)為 Drawable, 因此它的轉(zhuǎn)換器為 BitmapDrawableTranscoder

public class BitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, BitmapDrawable> {

  private final Resources resources;

  @Nullable
  @Override
  public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,
      @NonNull Options options) {
    // 調(diào)用了 LazyBitmapDrawableResource.obtain 獲取 Resource<BitmapDrawable> 的實(shí)例對(duì)象
    return LazyBitmapDrawableResource.obtain(resources, toTranscode);
  }
  
}

public final class LazyBitmapDrawableResource implements Resource<BitmapDrawable>,
    Initializable {

  public static Resource<BitmapDrawable> obtain(
      @NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
    ......
    // 創(chuàng)建了一個(gè) LazyBitmapDrawableResource
    return new LazyBitmapDrawableResource(resources, bitmapResource);
  }
  
  private LazyBitmapDrawableResource(@NonNull Resources resources,
      @NonNull Resource<Bitmap> bitmapResource) {
    this.resources = Preconditions.checkNotNull(resources);
    this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
  }
  
  public BitmapDrawable get() {
    // Get 方法反回了一個(gè) BitmapDrawable 對(duì)象
    return new BitmapDrawable(resources, bitmapResource.get());
  }

}

好的, 轉(zhuǎn)化成目標(biāo)數(shù)據(jù)也非常的簡(jiǎn)單, 它將我們解析到的 bitmap 存放到 LazyBitmapDrawableResource 內(nèi)部, 然后外界通過 get 方法就可以獲取到一個(gè) BitmapDrawable 的對(duì)象了

接下來看看 notifyEncodeAndRelease 如何展示數(shù)據(jù)的

2) 資源的緩存與展示
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
  
  private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    ......
    // 1. 回調(diào)上層資源準(zhǔn)備好了
    notifyComplete(result, dataSource);
    ......
    try {
      // 2. 將數(shù)據(jù)緩存到磁盤
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      ...
    }
  }
  
  private Callback<R> callback;
  
  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    ......
    // 1.1 從 DecodeJob 的構(gòu)建中, 我們知道這個(gè) Callback 是一 EngineJob
    callback.onResourceReady(resource, dataSource);
  }
  
}

好的, 可以看到 DecodeJob.decodeFromRetrievedData 中, 主要做了兩個(gè)操作

  • 回調(diào) EngineJob.onResourceReady 資源準(zhǔn)備好了
  • 將數(shù)據(jù)緩存到磁盤

這里我們主要看看 EngineJob.onResourceReady 中做了哪些處理

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

  @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) {
      ......
      engineResource = engineResourceFactory.build(resource, isCacheable);
      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);

      localKey = key;
      localResource = engineResource;
    }
    // 1. 通知上層 Engine 任務(wù)完成了
    listener.onEngineJobComplete(this, localKey, localResource);
    // 2. 回調(diào)給 ImageViewTarget 展示資源
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
  }
    
}

EngineJob 中也是有兩步操作

  • 一個(gè)是通知上層任務(wù)完成了
  • 另一個(gè)是回調(diào)給 ImageViewTarget 展示資源
Step1 回調(diào) Engine 任務(wù)完成

我們先看看上層做了什么處理

public class Engine implements EngineJobListener,
    MemoryCache.ResourceRemovedListener,
    EngineResource.ResourceListener {
  
  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    if (resource != null) {
      // 將加載好的資源添加到內(nèi)存緩存
      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }
    ......
  }
     
}

我們知道在請(qǐng)求發(fā)起前是 Engine 嘗試通過內(nèi)存緩存讀, 結(jié)束之后再回到 Engine 添加內(nèi)存緩存也不足為奇了

Step2 通知 ImageViewTarget 展示資源

接下來我們看看 ImageViewTarget 展示資源的過程

public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
    implements Transition.ViewAdapter {

  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    // 處理一些 transition 變化, 在構(gòu)建 Request 時(shí)有分析過, 這里不贅述其實(shí)現(xiàn)細(xì)節(jié)了
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      ......
    }
  }
  
  private void setResourceInternal(@Nullable Z resource) {
    // 調(diào)用了 setResource
    setResource(resource);
    ......
  }
 
}

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
    
  protected void setResource(@Nullable Drawable resource) {
    // 呈現(xiàn)到 View 上
    view.setImageDrawable(resource);
  }  
    
}

ImageViewTarget 調(diào)用了子類重寫的 setResource 方法, 將數(shù)據(jù)填充進(jìn)去, 至此一次 Glide 圖像加載就完成了

四) 流程回顧

請(qǐng)求加載流程回顧

走到這里, 一個(gè)請(qǐng)求的數(shù)據(jù)源獲取就已經(jīng)完成, 其流程如下

  • 任務(wù)的構(gòu)建
    • 構(gòu)建這個(gè)請(qǐng)求的 key
    • 從內(nèi)存緩存中查找 key 對(duì)應(yīng)的資源, 若存在直接回 onResourceReady 表示資源準(zhǔn)備好了
      • 從 ActiveResources 緩存中查找
      • 從 LruResourceCache 緩存中查找
    • 從緩存中查找 key 對(duì)應(yīng)的任務(wù)
      • 若存在則說明無需再次獲取資源
    • 構(gòu)建引擎任務(wù) EngineJob
      • 引擎的任務(wù)為解碼任務(wù) DecodeJob
      • 將任務(wù)添加到緩存, 防止多次構(gòu)建
      • 執(zhí)行 EngineJob
  • 任務(wù)的執(zhí)行
    • 通過 getNextStage 獲取 Decode 場(chǎng)景
    • 構(gòu)建當(dāng)前場(chǎng)景的處理器 DataFetcherGenerator
    • 調(diào)用 runGenerators 處理任務(wù)
      • 優(yōu)先從 Resource 的磁盤緩存中取數(shù)據(jù)
      • 次優(yōu)先從 Data 的磁盤緩存中取數(shù)據(jù)
      • 最終方案重新獲取 Source 數(shù)據(jù)
    • Source 通過 HttpUrlFetcher 獲取網(wǎng)絡(luò)數(shù)據(jù)流 InputStream
    • DecodeJob 的 onDataFetcherReady 處理 InputStream
      • 解析 InputStream
        • 通過 Downsampler 將 InputStream 解析成 Bitmap
        • 調(diào)用 Transformation.transformation 對(duì) Bitmap 進(jìn)行變化操作
        • 將 Bitmap 轉(zhuǎn)為 Drawable
      • 資源的緩存與展示
        • Engine 進(jìn)行內(nèi)存緩存
        • ViewTarget 展示資源
        • DecodeJob 進(jìn)行磁盤緩存
          • 原始 Bitmap 對(duì)應(yīng) SOURCE 類型
          • Transform 之后的 Bitmap 對(duì)應(yīng) RESOURCE 類型

總結(jié)

通過一次流程分析我們得知, 整個(gè) Glide 圖片加載主要有如下幾步

  • with
    • 獲取全局單例的 Glide 對(duì)象
    • 獲取全局單例的 RequestManagerRetriever
    • 創(chuàng)建當(dāng)前 Context 對(duì)應(yīng)的 RequestManager
  • load
    • 根據(jù)的寬高览芳、采樣的方式篱蝇、transform 變化等構(gòu)建一個(gè) RequestBuilder 對(duì)象
  • into
    • 根據(jù) ImageView 構(gòu)建采樣壓縮和圖像變化的策略保存在 Options 和 Transform 中
    • 構(gòu)建 ViewTarget, 描述這個(gè)請(qǐng)求要作用的 View 對(duì)象
    • 調(diào)用 into 重載方法
      • 構(gòu)建 Request 對(duì)象
      • 為 ViewTarget 綁定 Request
      • 交由 RequestManger 執(zhí)行該請(qǐng)求
  • 請(qǐng)求的執(zhí)行
    • 任務(wù)的構(gòu)建
      • 構(gòu)建這個(gè)請(qǐng)求的 key
      • 從內(nèi)存緩存中查找 key 對(duì)應(yīng)的資源, 若存在直接回 onResourceReady 表示資源準(zhǔn)備好了
        • 從 ActiveResources 緩存中查找
        • 從 LruResourceCache 緩存中查找
      • 從緩存中查找 key 對(duì)應(yīng)的任務(wù)
        • 若存在則說明無需再次獲取資源
      • 構(gòu)建引擎任務(wù) EngineJob
        • 引擎的任務(wù)為解碼任務(wù) DecodeJob
        • 將任務(wù)添加到緩存, 防止多次構(gòu)建
        • 執(zhí)行 EngineJob
    • 任務(wù)的執(zhí)行
      • 通過 getNextStage 獲取 Decode 場(chǎng)景
      • 構(gòu)建當(dāng)前場(chǎng)景的處理器 DataFetcherGenerator
      • 調(diào)用 runGenerators 處理任務(wù)
        • 優(yōu)先從 Resource 的磁盤緩存中取數(shù)據(jù)
        • 次優(yōu)先從 Data 的磁盤緩存中取數(shù)據(jù)
        • 最終方案重新獲取 Source 數(shù)據(jù)
      • Source 通過 HttpUrlFetcher 獲取網(wǎng)絡(luò)數(shù)據(jù)流 InputStream
        • DecodeJob 的 onDataFetcherReady 處理 InputStream
        • 解析 InputStream
          • 通過 Downsampler 將 InputStream 解析成 Bitmap
          • 調(diào)用 Transformation.transformation 對(duì) Bitmap 進(jìn)行變化操作
          • 將 Bitmap 轉(zhuǎn)為 BitmapDrawable
        • 資源的緩存與展示
          • Engine 進(jìn)行內(nèi)存緩存
          • ViewTarget 展示資源
          • DecodeJob 進(jìn)行磁盤緩存
            • 原始 Bitmap 對(duì)應(yīng) SOURCE 類型
            • Transform 之后的 Bitmap 對(duì)應(yīng) RESOURCE 類型

看了 Glide 的加載流程, 我似乎能夠明白為什么他是 Google 推薦的圖片加載框架了, 內(nèi)部細(xì)節(jié)的處理做的非常的到位, 而且使用 GlideContext 用于描述 Glide 的上下文, 與 Android 的 Context 巧妙的融合在一起, 讀起來真有一種閱讀 Android 源碼的既視感

不過這只是最簡(jiǎn)單的流程, 而且 Glide 支持 Gif, 視頻加載操作, 可想而知其內(nèi)部的 Decorder 處理了多少邏輯代碼, 如此復(fù)雜的流程, 嵌套了如此之多的回調(diào), 無疑增加了我們閱讀源碼的難度, 若是將這些操作分層, 并且使用攔截器去實(shí)現(xiàn), 我想定會(huì)讓一次圖像加載操作變得更加清晰明了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末艘狭,一起剝皮案震驚了整個(gè)濱河市棘伴,隨后出現(xiàn)的幾起案子光稼,更是在濱河造成了極大的恐慌诲锹,老刑警劉巖菩暗,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件状囱,死亡現(xiàn)場(chǎng)離奇詭異术裸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)浪箭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門穗椅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奶栖,你說我怎么就攤上這事匹表。” “怎么了宣鄙?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵袍镀,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我冻晤,道長(zhǎng)苇羡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任鼻弧,我火速辦了婚禮设江,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘攘轩。我一直安慰自己叉存,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布度帮。 她就那樣靜靜地躺著歼捏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笨篷。 梳的紋絲不亂的頭發(fā)上瞳秽,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音率翅,去河邊找鬼练俐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛冕臭,可吹牛的內(nèi)容都是我干的痰洒。 我是一名探鬼主播瓢棒,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼丘喻!你這毒婦竟也來了脯宿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤泉粉,失蹤者是張志新(化名)和其女友劉穎连霉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗡靡,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡跺撼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了讨彼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歉井。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哈误,靈堂內(nèi)的尸體忽然破棺而出哩至,到底是詐尸還是另有隱情,我是刑警寧澤蜜自,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布菩貌,位于F島的核電站,受9級(jí)特大地震影響重荠,放射性物質(zhì)發(fā)生泄漏箭阶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一戈鲁、第九天 我趴在偏房一處隱蔽的房頂上張望仇参。 院中可真熱鬧,春花似錦婆殿、人聲如沸诈乒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至暮蹂,卻和暖如春寞缝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仰泻。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工荆陆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人集侯。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓被啼,卻偏偏與公主長(zhǎng)得像帜消,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浓体,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353