Android 主流開源框架(六)Glide 的執(zhí)行流程源碼解析

前言

最近有個想法——就是把 Android 主流開源框架進(jìn)行深入分析骇窍,然后寫成一系列文章民宿,包括該框架的詳細(xì)使用與源碼解析。目的是通過鑒賞大神的源碼來了解框架底層的原理像鸡,也就是做到不僅要知其然活鹰,還要知其所以然。

這里我說下自己閱讀源碼的經(jīng)驗只估,我一般都是按照平時使用某個框架或者某個系統(tǒng)源碼的使用流程入手的志群,首先要知道怎么使用,然后再去深究每一步底層做了什么蛔钙,用了哪些好的設(shè)計模式锌云,為什么要這么設(shè)計。

系列文章:

更多干貨請關(guān)注 AndroidNotes

一吁脱、Glide 的基本使用示例

Glide 是一個快速高效的 Android 圖片加載庫桑涎,也是 Google 官方推薦的圖片加載庫。多數(shù)情況下兼贡,使用 Glide 加載圖片非常簡單攻冷,一行代碼就能解決。如下:

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

這行代碼用起來雖然簡單遍希,但是涉及到的三個方法 with()等曼、load()、into() 的內(nèi)部實現(xiàn)是比較復(fù)雜的凿蒜,接下來我們就根據(jù)這三個方法進(jìn)行源碼閱讀禁谦。這里沒有單獨用一篇文章來寫 Glide 的使用,是因為官方文檔已經(jīng)非常詳細(xì)了废封,看不懂英文的可以直接看中文的州泊。

二、Glide 源碼分析

這篇文章主要分析 Glide 的執(zhí)行流程漂洋,Glide 的緩存機(jī)制在下一篇文章中分析遥皂,這兩篇都是使用最新的 4.11.0 版本來分析。

因為 Glide 默認(rèn)是配置了內(nèi)存與磁盤緩存的氮发,所以這里我們先禁用內(nèi)存和磁盤緩存渴肉。如下設(shè)置:

Glide.with(this)
        .load(url)
        .skipMemoryCache(true) // 禁用內(nèi)存緩存
        .diskCacheStrategy(DiskCacheStrategy.NONE) // 禁用磁盤緩存
        .into(imageView);

注意:后面的分析都是加了跳過緩存的,所以你在跟著本文分析的時候記得加上上面兩句爽冕。

2.1 with()

with() 的重載方法有 6 個:

  • Glide#with(Context context)
  • Glide#with(Activity activity)
  • Glide#with(FragmentActivity activity)
  • Glide#with(Fragment fragment)
  • Glide#with(android.app.Fragment fragment)
  • Glide#with(View view)

這些方法的參數(shù)可以分成兩種情況仇祭,即 Application(Context)類型與非 Application(Activity、Fragment颈畸、View乌奇,這里的 View 獲取的是它所屬的 Activity 或 Fragment)類型没讲,這些參數(shù)的作用是確定圖片加載的生命周期。點擊進(jìn)去發(fā)現(xiàn) 6 個重載方法最終都會調(diào)用 getRetriever().get()礁苗,所以這里只拿 FragmentActivity 來演示:

  /*Glide*/
  public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }

2.1.1 Glide#getRetriever()

點擊 getRetriever() 方法進(jìn)去:

  /*Glide*/
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    ...
    return Glide.get(context).getRequestManagerRetriever();
  }

繼續(xù)看下 Glide#get(context):

  /*Glide*/
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      //(1)
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          //(2)
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }

    return glide;
  }

這里使用了雙重校驗鎖的單例模式來獲取 Glide 的實例爬凑,其中關(guān)注點(1)點擊進(jìn)去看看:

  /*Glide*/
  private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules(Context context) {
    GeneratedAppGlideModule result = null;
      Class<GeneratedAppGlideModule> clazz =
          (Class<GeneratedAppGlideModule>)
              Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl");
      result =
 clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext());
 
    ...
    
    return result;
  }

該方法用來實例化我們用 @GlideModule 注解標(biāo)識的自定義模塊,這里提一下自定義模塊的用法试伙。

  • Glide 3 用法
    定義一個類實現(xiàn) GlideModule嘁信,如下:
public class MyGlideModule implements GlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {

    }

    @Override
    public void registerComponents(Context context, Glide glide) {

    }
}

其中 applyOptions() 與 registerComponents() 方法分別用來更改 Glide 的配置以及替換 Glide 組件。

然后在 AndroidManifest.xml 文件中加入如下配置:

    <application>

        ...

        <meta-data
            android:name="com.wildma.myapplication.MyGlideModule"
            android:value="GlideModule" />
    </application>
  • Glide 4 用法
    定義一個類實現(xiàn) AppGlideModule疏叨,然后加上 @GlideModule 注解即可潘靖。如下:
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {

    @Override
    public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
    }

    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
    }
}

關(guān)于 @GlideModule 的更詳細(xì)使用可以查看 Generated API

接下來繼續(xù)查看關(guān)注點(2):

  /*Glide*/
  private static void checkAndInitializeGlide(
      @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
    // 不能重復(fù)初始化
    if (isInitializing) {
      throw new IllegalStateException(
          "You cannot call Glide.get() in registerComponents(),"
              + " use the provided Glide instance instead");
    }
    isInitializing = true;
    initializeGlide(context, generatedAppGlideModule);
    isInitializing = false;
  }
  
    /*Glide*/
    private static void initializeGlide(
      @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
    initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
  }
  
    /*Glide*/
  private static void initializeGlide(
      @NonNull Context context,
      @NonNull GlideBuilder builder,
      @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
    Context applicationContext = context.getApplicationContext();
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      //(1)
      manifestModules = new ManifestParser(applicationContext).parse();
    }

    ...

    // 從注解生成的 GeneratedAppGlideModule 中獲取 RequestManagerFactory
    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory()
            : null;
    // 將 RequestManagerFactory 設(shè)置到 GlideBuilder
    builder.setRequestManagerFactory(factory);
    //(2)
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    //(3)
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    //(4)
    Glide glide = builder.build(applicationContext);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      try {
        // (5)
        module.registerComponents(applicationContext, glide, glide.registry);
      } catch (AbstractMethodError e) {
        throw new IllegalStateException(
            "Attempting to register a Glide v3 module. If you see this, you or one of your"
                + " dependencies may be including Glide v3 even though you're using Glide v4."
                + " You'll need to find and remove (or update) the offending dependency."
                + " The v3 module name is: "
                + module.getClass().getName(),
            e);
      }
    }
    if (annotationGeneratedModule != null) {
      // (6)
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    // 注冊組件回調(diào)
    applicationContext.registerComponentCallbacks(glide);
    //(7)
    Glide.glide = glide;
  }

可以看到蚤蔓,調(diào)用 checkAndInitializeGlide() 方法后最終調(diào)用了最多參數(shù)的 initializeGlide() 方法卦溢。源碼中我標(biāo)記了 7 個關(guān)注點,分別如下:

  • (1):將 AndroidManifest.xml 中所有值為 GlideModule 的 meta-data 配置讀取出來秀又,并將相應(yīng)的自定義模塊實例化屈雄。也就是實例化前面演示的在 Glide 3 中的自定義模塊独令。

  • (2)(3):分別從兩個版本的自定義模塊中更改 Glide 的配置靠益。

  • (4):使用建造者模式創(chuàng)建 Glide疮鲫。

  • (5)(6):分別從兩個版本的自定義模塊中替換 Glide 組件。

  • (7):將創(chuàng)建的 Glide 賦值給 Glide 的靜態(tài)變量袱讹。

    繼續(xù)看下關(guān)注點(4)內(nèi)部是如何創(chuàng)建 Glide 的疲扎,進(jìn)入 GlideBuilder#build():

  /*GlideBuilder*/
  Glide build(@NonNull Context context) {
    if (sourceExecutor == null) {
      // 創(chuàng)建網(wǎng)絡(luò)請求線程池
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }

    if (diskCacheExecutor == null) {
      // 創(chuàng)建磁盤緩存線程池
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }

    if (animationExecutor == null) {
      // 創(chuàng)建動畫線程池
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }

    if (memorySizeCalculator == null) {
      // 創(chuàng)建內(nèi)存大小計算器
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }

    if (connectivityMonitorFactory == null) {
      // 創(chuàng)建默認(rèn)網(wǎng)絡(luò)連接監(jiān)視器工廠
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }

    // 創(chuàng)建 Bitmap 池
    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }

    if (arrayPool == null) {
      // 創(chuàng)建固定大小的數(shù)組池(4MB)昵时,使用 LRU 策略來保持?jǐn)?shù)組池在最大字節(jié)數(shù)以下
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }

    if (memoryCache == null) {
      // 創(chuàng)建內(nèi)存緩存
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }

    if (diskCacheFactory == null) {捷雕、
      // 創(chuàng)建磁盤緩存工廠
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }

    /*創(chuàng)建加載以及管理活動資源和緩存資源的引擎*/
    if (engine == null) {
      engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              animationExecutor,
              isActiveResourceRetentionAllowed);
    }

    if (defaultRequestListeners == null) {
      defaultRequestListeners = Collections.emptyList();
    } else {
      defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
    }

    // 創(chuàng)建請求管理類,這里的 requestManagerFactory 就是前面 GlideBuilder#setRequestManagerFactory() 設(shè)置進(jìn)來的
    // 也就是 @GlideModule 注解中獲取的
    RequestManagerRetriever requestManagerRetriever =
        new RequestManagerRetriever(requestManagerFactory);

    //(1)創(chuàng)建 Glide
    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptionsFactory,
        defaultTransitionOptions,
        defaultRequestListeners,
        isLoggingRequestOriginsEnabled,
        isImageDecoderEnabledForBitmaps);
  }

可以看到壹甥,build() 方法主要是創(chuàng)建一些線程池救巷、Bitmap 池、緩存策略句柠、Engine 等浦译,然后利用這些來創(chuàng)建具體的 Glide。繼續(xù)看下 Glide 的構(gòu)造函數(shù):

  /*Glide*/
  Glide(...) {
    /*將傳進(jìn)來的參數(shù)賦值給 Glide 類中的一些常量溯职,方便后續(xù)使用精盅。*/
    this.engine = engine;
    this.bitmapPool = bitmapPool;
    this.arrayPool = arrayPool;
    this.memoryCache = memoryCache;
    this.requestManagerRetriever = requestManagerRetriever;
    this.connectivityMonitorFactory = connectivityMonitorFactory;
    this.defaultRequestOptionsFactory = defaultRequestOptionsFactory;

    final Resources resources = context.getResources();

    // 創(chuàng)建 Registry,Registry 的作用是管理組件注冊谜酒,用來擴(kuò)展或替換 Glide 的默認(rèn)加載叹俏、解碼和編碼邏輯。
    registry = new Registry();
    
    // 省略的部分主要是:創(chuàng)建一些處理圖片的解析器僻族、解碼器粘驰、轉(zhuǎn)碼器等屡谐,然后將他們添加到 Registry 中
    ...

    // 創(chuàng)建 ImageViewTargetFactory,用來給 View 獲取正確類型的 ViewTarget(BitmapImageViewTarget 或 DrawableImageViewTarget)
    ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
    // 構(gòu)建一個 Glide 專屬的上下文
    glideContext =
        new GlideContext(
            context,
            arrayPool,
            registry,
            imageViewTargetFactory,
            defaultRequestOptionsFactory,
            defaultTransitionOptions,
            defaultRequestListeners,
            engine,
            isLoggingRequestOriginsEnabled,
            logLevel);
  }

到這一步蝌数,Glide 才算真正創(chuàng)建成功愕掏。也就是 Glide.get(context).getRequestManagerRetriever() 中的 get() 方法已經(jīng)走完了。

接下來看下 getRequestManagerRetriever() 方法:

    /*Glide*/
    public RequestManagerRetriever getRequestManagerRetriever() {
    return requestManagerRetriever;
  }

發(fā)現(xiàn)這里直接返回了實例顶伞,其實 RequestManagerRetriever 的實例在前面的 GlideBuilder#build() 方法中已經(jīng)創(chuàng)建了饵撑,所以這里可以直接返回。

到這一步唆貌,getRetriever(activity).get(activity) 中的 getRetriever() 方法也已經(jīng)走完了肄梨,并返回了 RequestManagerRetriever,接下來繼續(xù)看下 get() 方法挠锥。

2.1.2 RequestManagerRetriever#get()

RequestManagerRetriever#get() 的重載方法同樣有 6 個:

  • RequestManagerRetriever#get(Context context)
  • RequestManagerRetriever#get(Activity activity)
  • RequestManagerRetriever#get(FragmentActivity activity)
  • RequestManagerRetriever#get(Fragment fragment)
  • RequestManagerRetriever#get(android.app.Fragment fragment)
  • RequestManagerRetriever#get(View view)

這些方法的參數(shù)同樣可以分成兩種情況众羡,即 Application 與非 Application 類型。先看下 Application 類型的情況:

  /*RequestManagerRetriever*/
  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)*/
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper
          && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }

    //(2)
    return getApplicationManager(context);
  }

這里標(biāo)注了 2 個關(guān)注點蓖租,分別如下:

  • (1):判斷如果當(dāng)前線程是在主線程粱侣,并且 context 不屬于 Application 類型,那么會走對應(yīng)重載方法蓖宦。
  • (2):屬于 Application 類型齐婴,就會調(diào)用 getApplicationManager(context) 方法,點進(jìn)去看看:
  /*RequestManagerRetriever*/
  private RequestManager getApplicationManager(@NonNull Context context) {
    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;
  }

可以看到稠茂,這里使用了單例模式來獲取 RequestManager柠偶,里面重新用 Application 類型的 Context 來獲取 Glide 的實例。這里并沒有專門做生命周期的處理睬关,
因為 Application 對象的生命周期即為應(yīng)用程序的生命周期诱担,所以在這里圖片請求的生命周期是和應(yīng)用程序同步的。

我們繼續(xù)看下非 Application 類型的情況电爹,這里只拿 FragmentActivity 來講蔫仙,其他類似。代碼如下:

  /*RequestManagerRetriever*/
  public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      // 檢查 Activity 是否銷毀
      assertNotDestroyed(activity);
      // 獲取當(dāng)前 Activity 的 FragmentManager
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

這里判斷如果是后臺線程那么還是走上面的 Application 類型的 get() 方法丐箩,否則調(diào)用 supportFragmentGet() 方法來獲取 RequestManager摇邦。
看下 supportFragmentGet() 方法:

  /*RequestManagerRetriever*/
  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    //(1)
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      Glide glide = Glide.get(context);
      //(2)
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      //(3)
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

  /*RequestManagerRetriever*/
  // 關(guān)注點(1)調(diào)用了 getSupportRequestManagerFragment() 方法
  private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingSupportRequestManagerFragments.get(fm);
      if (current == null) {
        //(4)
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        pendingSupportRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

  /*SupportRequestManagerFragment*/
  // 關(guān)注點(4)實例化了 SupportRequestManagerFragment
  public SupportRequestManagerFragment() {
    //(5)
    this(new ActivityFragmentLifecycle());
  }

在 supportFragmentGet() 方法中我標(biāo)注了 3 個關(guān)注點,分別如下:

  • (1):獲取了一個隱藏的 Fragment(在關(guān)注點(4))屎勘,在實例化 SupportRequestManagerFragment 的時候又實例化了
    ActivityFragmentLifecycle(在關(guān)注點(5))施籍,ActivityFragmentLifecycle 實現(xiàn)了 Lifecycle,主要用來監(jiān)聽 Activity 與 Fragment 的生命周期概漱。
  • (2):這里的 current.getGlideLifecycle() 就是關(guān)注點(1)中實例化的 ActivityFragmentLifecycle丑慎,這樣 RequestManager 就與 ActivityFragmentLifecycle 進(jìn)行了關(guān)聯(lián)。
  • (3):將 RequestManager 設(shè)置到 SupportRequestManagerFragment 中。

所以經(jīng)過(2)(3)實際是將 SupportRequestManagerFragment立哑、RequestManager夜惭、ActivityFragmentLifecycle 都關(guān)聯(lián)在一起了,又因為 Fragment 的生命周期和 Activity 是同步的铛绰,
所以 Activity 生命周期發(fā)生變化的時候诈茧,隱藏的 Fragment 的生命周期是同步變化的,這樣 Glide 就可以根據(jù)這個 Fragment 的生命周期進(jìn)行請求管理了捂掰。

是不是這樣的呢敢会?我們?nèi)タ纯?SupportRequestManagerFragment 的生命周期就知道了。

  /*SupportRequestManagerFragment*/
  @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

  @Override
  public void onStop() {
    super.onStop();
    lifecycle.onStop();
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
  }

可以看到这嚣,ActivityFragmentLifecycle 確實與 SupportRequestManagerFragment 的生命周期關(guān)聯(lián)起來了鸥昏,我們這里只拿 onDestroy 來看看,
這里調(diào)用了 lifecycle#onDestroy()姐帚,間接調(diào)用了如下方法:

  /*ActivityFragmentLifecycle*/
  void onDestroy() {
    isDestroyed = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onDestroy();
    }
  }

  /*LifecycleListener*/
  void onDestroy();

  /*RequestManager*/
  @Override
  public synchronized void onDestroy() {
    targetTracker.onDestroy();
    for (Target<?> target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    mainHandler.removeCallbacks(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
  }

可以看到吏垮,因為 RequestManager 是實現(xiàn)了 LifecycleListener 接口的,所以最終是調(diào)用了 RequestManager 的 onDestroy() 方法罐旗,
該方法里面主要做了取消所有進(jìn)行中的請求并清除和回收所有已完成請求的資源膳汪。確實如我們所料,圖片請求的生命周期就是由一個隱藏的 Fragment 的生命周期決定的九秀。

這里總結(jié)下遗嗽,RequestManagerRetriever#get() 方法如果傳入 Application 類型的參數(shù),那么圖片請求的生命周期就是 Application 對象的生命周期鼓蜒,即應(yīng)用程序的生命周期痹换。
如果傳入的是非 Application 類型的參數(shù),那么會向當(dāng)前的 Activity 當(dāng)中添加一個隱藏的 Fragment都弹,然后圖片請求的生命周期由這個 Fragment 的生命周期決定娇豫。

2.1.3 小結(jié)

with() 方法主要做了如下事情:

  • 獲取 AndroidManifest.xml 文件中配置的自定義模塊與 @GlideModule 注解標(biāo)識的自定義模塊,然后進(jìn)行 Glide 配置的更改與組件的替換缔杉。
  • 初始化構(gòu)建 Glide 實例需要的各種配置信息锤躁,例如線程池、Bitmap 池或详、緩存策略、Engine 等郭计,然后利用這些來創(chuàng)建具體的 Glide霸琴。
  • 將 Glide 的請求與 Application 或者隱藏的 Fragment 的生命周期進(jìn)行綁定。

2.2 load()

load() 的重載方法有 9 個:

  • RequestManager#load(Bitmap bitmap);
  • RequestManager#load(Drawable drawable);
  • RequestManager#load(String string);
  • RequestManager#load(Uri uri);
  • RequestManager#load(File file);
  • RequestManager#load(Integer resourceId);
  • RequestManager#load(URL url);
  • RequestManager#load(byte[] model);
  • RequestManager#load(Object model);

點擊進(jìn)去發(fā)現(xiàn)這些重載方法最終都會調(diào)用 asDrawable().load()昭伸,所以這里只拿參數(shù)為字符串的來演示梧乘,也就是參數(shù)為圖片鏈接。如下:

  /*RequestManager*/
  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

2.2.1 RequestManager#asDrawable()

點擊 asDrawable() 方法進(jìn)去看看:

  /*RequestManager*/
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }

  /*RequestManager*/
  public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }

  /*RequestBuilder*/
  protected RequestBuilder(
      @NonNull Glide glide,
      RequestManager requestManager,
      Class<TranscodeType> transcodeClass,
      Context context) {
    /*給 RequestBuilder 中的一些常量進(jìn)行賦值*/
    this.glide = glide;
    this.requestManager = requestManager;
    this.transcodeClass = transcodeClass;
    this.context = context;
    this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
    this.glideContext = glide.getGlideContext();

    // 初始化請求監(jiān)聽
    initRequestListeners(requestManager.getDefaultRequestListeners());
    //(1)
    apply(requestManager.getDefaultRequestOptions());
  }

可以看到,經(jīng)過一些列調(diào)用选调,該方法最終創(chuàng)建了 RequestBuilder 的實例并返回夹供。其中關(guān)注點(1)是將默認(rèn)選項應(yīng)用于請求,點進(jìn)去看看里面做了什么:

  /*RequestBuilder*/
  @Override
  public RequestBuilder<TranscodeType> apply(@NonNull BaseRequestOptions<?> requestOptions) {
    Preconditions.checkNotNull(requestOptions);
    return super.apply(requestOptions);
  }

這里又調(diào)用了父類的 apply() 方法:

  /*BaseRequestOptions*/
  public T apply(@NonNull BaseRequestOptions<?> o) {
    if (isAutoCloneEnabled) {
      return clone().apply(o);
    }
    BaseRequestOptions<?> other = o;

    if (isSet(other.fields, SIZE_MULTIPLIER)) {
      sizeMultiplier = other.sizeMultiplier;
    }
    if (isSet(other.fields, USE_UNLIMITED_SOURCE_GENERATORS_POOL)) {
      useUnlimitedSourceGeneratorsPool = other.useUnlimitedSourceGeneratorsPool;
    }
    if (isSet(other.fields, USE_ANIMATION_POOL)) {
      useAnimationPool = other.useAnimationPool;
    }
    if (isSet(other.fields, DISK_CACHE_STRATEGY)) {
      diskCacheStrategy = other.diskCacheStrategy;
    }
    if (isSet(other.fields, PRIORITY)) {
      priority = other.priority;
    }
    if (isSet(other.fields, ERROR_PLACEHOLDER)) {
      errorPlaceholder = other.errorPlaceholder;
      errorId = 0;
      fields &= ~ERROR_ID;
    }
    if (isSet(other.fields, ERROR_ID)) {
      errorId = other.errorId;
      errorPlaceholder = null;
      fields &= ~ERROR_PLACEHOLDER;
    }
    if (isSet(other.fields, PLACEHOLDER)) {
      placeholderDrawable = other.placeholderDrawable;
      placeholderId = 0;
      fields &= ~PLACEHOLDER_ID;
    }
    if (isSet(other.fields, PLACEHOLDER_ID)) {
      placeholderId = other.placeholderId;
      placeholderDrawable = null;
      fields &= ~PLACEHOLDER;
    }
    if (isSet(other.fields, IS_CACHEABLE)) {
      isCacheable = other.isCacheable;
    }
    if (isSet(other.fields, OVERRIDE)) {
      overrideWidth = other.overrideWidth;
      overrideHeight = other.overrideHeight;
    }
    if (isSet(other.fields, SIGNATURE)) {
      signature = other.signature;
    }
    if (isSet(other.fields, RESOURCE_CLASS)) {
      resourceClass = other.resourceClass;
    }
    if (isSet(other.fields, FALLBACK)) {
      fallbackDrawable = other.fallbackDrawable;
      fallbackId = 0;
      fields &= ~FALLBACK_ID;
    }
    if (isSet(other.fields, FALLBACK_ID)) {
      fallbackId = other.fallbackId;
      fallbackDrawable = null;
      fields &= ~FALLBACK;
    }
    if (isSet(other.fields, THEME)) {
      theme = other.theme;
    }
    if (isSet(other.fields, TRANSFORMATION_ALLOWED)) {
      isTransformationAllowed = other.isTransformationAllowed;
    }
    if (isSet(other.fields, TRANSFORMATION_REQUIRED)) {
      isTransformationRequired = other.isTransformationRequired;
    }
    if (isSet(other.fields, TRANSFORMATION)) {
      transformations.putAll(other.transformations);
      isScaleOnlyOrNoTransform = other.isScaleOnlyOrNoTransform;
    }
    if (isSet(other.fields, ONLY_RETRIEVE_FROM_CACHE)) {
      onlyRetrieveFromCache = other.onlyRetrieveFromCache;
    }

    // Applying options with dontTransform() is expected to clear our transformations.
    if (!isTransformationAllowed) {
      transformations.clear();
      fields &= ~TRANSFORMATION;
      isTransformationRequired = false;
      fields &= ~TRANSFORMATION_REQUIRED;
      isScaleOnlyOrNoTransform = true;
    }

    fields |= other.fields;
    options.putAll(other.options);

    return selfOrThrowIfLocked();
  }

可以看到仁堪,配置選項有很多哮洽,包括磁盤緩存策略,加載中的占位圖弦聂,加載失敗的占位圖等鸟辅。
到這里 asDrawable() 方法就看完了,接下來繼續(xù)看 asDrawable().load(string) 中的 load() 方法莺葫。

2.2.2 RequestBuilder#load()

點擊 load() 方法進(jìn)去看看:

  /*RequestBuilder*/
  @Override
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }

  /*RequestBuilder*/
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

發(fā)現(xiàn)這個方法非常簡單匪凉,首先調(diào)用了 loadGeneric() 方法,然后 loadGeneric() 方法中將傳進(jìn)來的圖片資源賦值給了變量 model捺檬,最后用 isModelSet 標(biāo)記已經(jīng)調(diào)用過 load() 方法了再层。

2.2.3 小結(jié)

load() 方法就比較簡單了,主要是通過前面實例化的 Glide 與 RequestManager 來創(chuàng)建 RequestBuilder堡纬,然后將傳進(jìn)來的參數(shù)賦值給 model树绩。接下來主要看看 into() 方法。

2.3 into()

我們發(fā)現(xiàn)隐轩,前兩個方法都沒有涉及到圖片的請求饺饭、緩存、解碼等邏輯职车,其實都在 into() 方法中瘫俊,所以這個方法也是最復(fù)雜的。

點擊 into() 方法進(jìn)去看看:

  /*RequestBuilder*/
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    BaseRequestOptions<?> requestOptions = this;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      /*將 ImageView 的 scaleType 設(shè)置給 BaseRequestOptions(ImageView 的默認(rèn) scaleType 為 fitCenter)*/
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }
    //(1)buildImageViewTarget()
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }
  
  /*RequestBuilder*/
  private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    //(2)
    Request request = buildRequest(target, targetListener, options, callbackExecutor);

    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target);
    target.setRequest(request);
    //(3)
    requestManager.track(target, request);

    return target;
  }

可以看到悴灵,里面又調(diào)用了 into() 的另一個重載方法扛芽。這里我標(biāo)記了 3 個關(guān)注點,分別是獲取 ImageViewTarget积瞒、構(gòu)建請求和執(zhí)行請求川尖,后面我們就主要分析這 3 個關(guān)注點。

2.3.1 GlideContext#buildImageViewTarget()

點擊 RequestBuilder#into() 中的關(guān)注點(1)進(jìn)去看看:

  /*GlideContext*/
  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }

  /*ImageViewTargetFactory*/
  public <Z> ViewTarget<ImageView, Z> buildTarget(
      @NonNull ImageView view, @NonNull Class<Z> clazz) {
    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 {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }

可以看到茫孔,這里是通過 ImageViewTargetFactory#buildTarget(imageView, transcodeClass) 來獲取的叮喳,其中 ImageViewTargetFactory 是在 Glide 的構(gòu)造函數(shù)中實例化的,而 transcodeClass 是 load() 方法中 asDrawable()--as(Drawable.class) 傳進(jìn)來的缰贝。然后 buildTarget() 方法中就是根據(jù)這個 transcodeClass 來返回對應(yīng)的 ViewTarget馍悟,所以默認(rèn)情況下都是返回 DrawableImageViewTarget,只有專門指定 asBitmap() 才會返回 BitmapImageViewTarget剩晴,如下指定:

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

接下來繼續(xù)看關(guān)注點(2)锣咒。

2.3.2 RequestBuilder#buildRequest()

點擊 RequestBuilder#into() 中的關(guān)注點(2)進(jìn)去看看:

  /*RequestBuilder*/
  private Request buildRequest(...) {
    return buildRequestRecursive(
        /*requestLock=*/ new Object(),
        target,
        targetListener,
        /*parentCoordinator=*/ null,
        transitionOptions,
        requestOptions.getPriority(),
        requestOptions.getOverrideWidth(),
        requestOptions.getOverrideHeight(),
        requestOptions,
        callbackExecutor);
  }

這里又調(diào)用了 buildRequestRecursive() 方法:

  /*RequestBuilder*/
  private Request buildRequestRecursive(...) {

    // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
    ErrorRequestCoordinator errorRequestCoordinator = null;
    //(1)
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }

    //(2)遞歸構(gòu)建縮略圖請求
    Request mainRequest =
        buildThumbnailRequestRecursive(...);

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

    //(3)遞歸構(gòu)建錯誤請求
    Request errorRequest =
        errorBuilder.buildRequestRecursive(...);
    errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
  }

可以看到侵状,只有關(guān)注點(1)中的 errorBuilder 不為空,即我們設(shè)置了在主請求失敗時開始新的請求(如下設(shè)置) 才會走到關(guān)注點(3)去遞歸構(gòu)建錯誤請求毅整。

// 設(shè)置在主請求失敗時開始新的請求
Glide.with(this).load(url).error(Glide.with(this).load(fallbackUrl)).into(imageView);

因為我什么都沒有設(shè)置趣兄,所以這里看關(guān)注點(2)的遞歸構(gòu)建縮略圖請求即可。

點擊 buildThumbnailRequestRecursive() 方法進(jìn)去看看:

  /*RequestBuilder*/
  private Request buildThumbnailRequestRecursive(
      Object requestLock,
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {
    if (thumbnailBuilder != null) { //(1)
      
      ...

      //(1.1)
      ThumbnailRequestCoordinator coordinator =
          new ThumbnailRequestCoordinator(requestLock, parentCoordinator);
      //(1.2)
      Request fullRequest =
          obtainRequest(
              requestLock,
              target,
              targetListener,
              requestOptions,
              coordinator,
              transitionOptions,
              priority,
              overrideWidth,
              overrideHeight,
              callbackExecutor);
      isThumbnailBuilt = true;
      // Recursively generate thumbnail requests.
      //(1.3)
      Request thumbRequest =
          thumbnailBuilder.buildRequestRecursive(
              requestLock,
              target,
              targetListener,
              coordinator,
              thumbTransitionOptions,
              thumbPriority,
              thumbOverrideWidth,
              thumbOverrideHeight,
              thumbnailBuilder,
              callbackExecutor);
      isThumbnailBuilt = false;
      coordinator.setRequests(fullRequest, thumbRequest);
      return coordinator;
    } else if (thumbSizeMultiplier != null) { //(2)
      // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
      ThumbnailRequestCoordinator coordinator =
          new ThumbnailRequestCoordinator(requestLock, parentCoordinator);
      Request fullRequest =
          obtainRequest(
              requestLock,
              target,
              targetListener,
              requestOptions,
              coordinator,
              transitionOptions,
              priority,
              overrideWidth,
              overrideHeight,
              callbackExecutor);
      BaseRequestOptions<?> thumbnailOptions =
          requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);

      Request thumbnailRequest =
          obtainRequest(
              requestLock,
              target,
              targetListener,
              thumbnailOptions,
              coordinator,
              transitionOptions,
              getThumbnailPriority(priority),
              overrideWidth,
              overrideHeight,
              callbackExecutor);

      coordinator.setRequests(fullRequest, thumbnailRequest);
      return coordinator;
    } else { //(3)
      // Base case: no thumbnail.
      return obtainRequest(
          requestLock,
          target,
          targetListener,
          requestOptions,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight,
          callbackExecutor);
    }
  }

可以看到悼嫉,這里我標(biāo)記了 3 個大的關(guān)注點艇潭,分別如下:

  • (1):設(shè)置了縮略圖請求的時候會走這里,如下設(shè)置:
// 設(shè)置縮略圖請求
Glide.with(this).load(url).thumbnail(Glide.with(this).load(thumbnailUrl)).into(imageView);

其中關(guān)注點(1.2)是獲取一個原圖請求承粤,關(guān)注點(1.3)是根據(jù)設(shè)置的 thumbnailBuilder 來生成縮略圖請求暴区,然后關(guān)注點(1.1)是創(chuàng)建一個協(xié)調(diào)器,用來協(xié)調(diào)這兩個請求辛臊,這樣可以同時進(jìn)行原圖與縮略圖的請求仙粱。

  • (2):設(shè)置了縮略圖的縮略比例的時候會走這里,如下設(shè)置:
// 設(shè)置縮略圖的縮略比例
Glide.with(this).load(url).thumbnail(0.5f).into(imageView);

與關(guān)注點(1)一樣彻舰,也是通過一個協(xié)調(diào)器來同時進(jìn)行原圖與縮略圖的請求伐割,不同的是這里生成縮略圖用的是縮略比例。

  • (3):沒有縮略圖相關(guān)設(shè)置刃唤,直接獲取原圖請求隔心。

因為我什么都沒有設(shè)置,所以這里看關(guān)注點(3)即可尚胞。點擊 obtainRequest() 方法進(jìn)去看看:

  /*RequestBuilder*/
  private Request obtainRequest(...) {
    return SingleRequest.obtain(...);
  }

  /*SingleRequest*/
  public static <R> SingleRequest<R> obtain(...) {
    return new SingleRequest<>(...);
  }

  /*SingleRequest*/
  private SingleRequest(
      Context context,
      GlideContext glideContext,
      @NonNull Object requestLock,
      @Nullable Object model,
      Class<R> transcodeClass,
      BaseRequestOptions<?> requestOptions,
      int overrideWidth,
      int overrideHeight,
      Priority priority,
      Target<R> target,
      @Nullable RequestListener<R> targetListener,
      @Nullable List<RequestListener<R>> requestListeners,
      RequestCoordinator requestCoordinator,
      Engine engine,
      TransitionFactory<? super R> animationFactory,
      Executor callbackExecutor) {
    this.requestLock = requestLock;
    this.context = context;
    this.glideContext = glideContext;
    this.model = model;
    this.transcodeClass = transcodeClass;
    this.requestOptions = requestOptions;
    this.overrideWidth = overrideWidth;
    this.overrideHeight = overrideHeight;
    this.priority = priority;
    this.target = target;
    this.targetListener = targetListener;
    this.requestListeners = requestListeners;
    this.requestCoordinator = requestCoordinator;
    this.engine = engine;
    this.animationFactory = animationFactory;
    this.callbackExecutor = callbackExecutor;
    status = Status.PENDING;

    if (requestOrigin == null && glideContext.isLoggingRequestOriginsEnabled()) {
      requestOrigin = new RuntimeException("Glide request origin trace");
    }
  }

可以看到硬霍,最終是實例化了一個 SingleRequest 的實例,也就是說構(gòu)建請求這一步已經(jīng)完成了笼裳,接下來分析執(zhí)行請求這一步唯卖。

2.3.3 RequestManager#track()

點擊 RequestBuilder#into() 中的關(guān)注點(3)進(jìn)去看看:

  /*RequestManager*/
  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }

點擊 track() 進(jìn)去:

public final class TargetTracker implements LifecycleListener {
  private final Set<Target<?>> targets =
      Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());

  public void track(@NonNull Target<?> target) {
    targets.add(target);
  }
}

可以看到,targetTracker.track(target) 是將一個 target 加入 Set 集合中躬柬,繼續(xù)看下 runRequest() 方法:

  /*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) {
      request.begin();
    } else {
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);
    }
  }

可以看到拜轨,首先將請求加入一個 Set 集合,然后判斷是否暫停狀態(tài)允青,是則清空請求橄碾,并將該請求加入待處理的請求集合中;不是暫停狀態(tài)則開始請求颠锉。

點擊 begin() 方法進(jìn)去看看:

  /*SingleRequest*/
  @Override
  public void begin() {
    synchronized (requestLock) {
      assertNotCallingCallbacks();
      stateVerifier.throwIfRecycled();
      startTime = LogTime.getLogTime();
      /*如果加載的資源為空法牲,則調(diào)用加載失敗的回調(diào)*/
      if (model == null) {
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
          width = overrideWidth;
          height = overrideHeight;
        }

        int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
        onLoadFailed(new GlideException("Received null model"), logLevel);
        return;
      }

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

      if (status == Status.COMPLETE) {
        // 資源加載完成
        onResourceReady(resource, DataSource.MEMORY_CACHE);
        return;
      }

      status = Status.WAITING_FOR_SIZE;
      //(1)
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        target.getSize(this);
      }

      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) {
        // 剛開始加載的回調(diào)
        target.onLoadStarted(getPlaceholderDrawable());
      }
      if (IS_VERBOSE_LOGGABLE) {
        logV("finished run method in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }

如上,關(guān)鍵代碼在關(guān)注點(1)木柬,這里做了一個判斷皆串,如果我們設(shè)置了 overrideWidth 和 overrideHeight(如下設(shè)置),則直接調(diào)用 onSizeReady() 方法眉枕,否則調(diào)用 getSize() 方法(第一次加載才會調(diào)用)。

// 設(shè)置加載圖片的寬高為 100x100 px
Glide.with(this).load(url).override(100,100).into(imageView);

getSize() 方法的作用是通過 ViewTreeObserver 來監(jiān)聽 ImageView 的寬高,拿到寬高后最終也是調(diào)用 onSizeReady() 方法速挑。

接下來看下 onSizeReady() 方法:

  /*SingleRequest*/
  @Override
  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {

      ...

      /*根據(jù)縮略比例獲取圖片寬高*/
      float sizeMultiplier = requestOptions.getSizeMultiplier();
      this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
      this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

      // 開始加載
      loadStatus =
          engine.load(
              glideContext,
              model,
              requestOptions.getSignature(),
              this.width,
              this.height,
              requestOptions.getResourceClass(),
              transcodeClass,
              priority,
              requestOptions.getDiskCacheStrategy(),
              requestOptions.getTransformations(),
              requestOptions.isTransformationRequired(),
              requestOptions.isScaleOnlyOrNoTransform(),
              requestOptions.getOptions(),
              requestOptions.isMemoryCacheable(),
              requestOptions.getUseUnlimitedSourceGeneratorsPool(),
              requestOptions.getUseAnimationPool(),
              requestOptions.getOnlyRetrieveFromCache(),
              this,
              callbackExecutor);
      
      ...

    }
  }

可以看到谤牡,這里開始加載了,繼續(xù)跟進(jìn):

  /*Engine*/
  public <R> LoadStatus load(
      GlideContext glideContext,
      Object model,
      Key signature,
      int width,
      int height,
      Class<?> resourceClass,
      Class<R> transcodeClass,
      Priority priority,
      DiskCacheStrategy diskCacheStrategy,
      Map<Class<?>, Transformation<?>> transformations,
      boolean isTransformationRequired,
      boolean isScaleOnlyOrNoTransform,
      Options options,
      boolean isMemoryCacheable,
      boolean useUnlimitedSourceExecutorPool,
      boolean useAnimationPool,
      boolean onlyRetrieveFromCache,
      ResourceCallback cb,
      Executor callbackExecutor) {
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;

    // 構(gòu)建緩存 key
    EngineKey key =
        keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);

    EngineResource<?> memoryResource;
    synchronized (this) {
      // 從內(nèi)存中加載資源
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);

      // 內(nèi)存中沒有姥宝,則等待當(dāng)前正在執(zhí)行的或開始一個新的 EngineJob
      if (memoryResource == null) {
        return waitForExistingOrStartNewJob(
            glideContext,
            model,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            options,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache,
            cb,
            callbackExecutor,
            key,
            startTime);
      }
    }

    // 加載完成回調(diào)
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
    return null;
  }

可以看到翅萤,這里首先構(gòu)建緩存 key,然后利用這個 key 獲取緩存中的資源腊满,如果內(nèi)存中沒有套么,則等待當(dāng)前正在執(zhí)行的或開始一個新的 EngineJob,內(nèi)存中有則調(diào)用加載完成的回調(diào)碳蛋。這里我們先不討論緩存相關(guān)的胚泌,下一篇文章再講。所以默認(rèn)是第一次加載肃弟,點擊 waitForExistingOrStartNewJob() 方法進(jìn)去看看:

  /*Engine*/
  private <R> LoadStatus waitForExistingOrStartNewJob(
      GlideContext glideContext,
      Object model,
      Key signature,
      int width,
      int height,
      Class<?> resourceClass,
      Class<R> transcodeClass,
      Priority priority,
      DiskCacheStrategy diskCacheStrategy,
      Map<Class<?>, Transformation<?>> transformations,
      boolean isTransformationRequired,
      boolean isScaleOnlyOrNoTransform,
      Options options,
      boolean isMemoryCacheable,
      boolean useUnlimitedSourceExecutorPool,
      boolean useAnimationPool,
      boolean onlyRetrieveFromCache,
      ResourceCallback cb,
      Executor callbackExecutor,
      EngineKey key,
      long startTime) {

    //(1)
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb, callbackExecutor);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }

    //(2)
    EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);

    //(3)
    DecodeJob<R> decodeJob =
        decodeJobFactory.build(
            glideContext,
            model,
            key,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            onlyRetrieveFromCache,
            options,
            engineJob);

    // 將 EngineJob 放到一個 map 集合中
    jobs.put(key, engineJob);
    // 添加回調(diào)
    engineJob.addCallback(cb, callbackExecutor);
    //(4)
    engineJob.start(decodeJob);

    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    // 返回加載狀態(tài)
    return new LoadStatus(cb, engineJob);
  }

這里標(biāo)記了 4 個關(guān)注點玷室,分別如下:

  • (1):從 map 集合中獲取 EngineJob,如果不為空表示當(dāng)前有正在執(zhí)行的 EngineJob笤受,添加回調(diào)并返回加載狀態(tài)穷缤。
  • (2):使用 EngineJob 工廠構(gòu)建了一個 EngineJob,該類主要用來管理加載以及當(dāng)加載完成時通知回調(diào)箩兽。
  • (3):使用 DecodeJob 工廠構(gòu)建了一個 DecodeJob津肛,該類主要負(fù)責(zé)圖片的解碼,實現(xiàn)了 Runnable 接口汗贫,屬于一個任務(wù)身坐。
  • (4):這里調(diào)用了 EngineJob#start(),點擊進(jìn)去看看:
  /*EngineJob*/
  public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor =
        decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }

這里直接將 DecodeJob 任務(wù)放到了一個線程池中去執(zhí)行芳绩,也就是說從這里開始切換到子線程了掀亥,那么我們看下 DecodeJob 的 run() 方法做了什么:

  /*DecodeJob*/
  @Override
  public void run() {
    GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
    DataFetcher<?> localFetcher = currentFetcher;
    try {
      // 被取消,則調(diào)用加載失敗的回調(diào)
      if (isCancelled) {
        notifyFailed();
        return;
      }
      // 執(zhí)行
      runWrapped();
    } catch (CallbackException e) {
      throw e;
    } catch (Throwable t) {

    ...

    } finally {
      // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
      // close in all cases anyway.
      if (localFetcher != null) {
        localFetcher.cleanup();
      }
      GlideTrace.endSection();
    }
  }

繼續(xù) runWrapped() 方法:

  /*DecodeJob*/
  private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        /*(4.1)*/
        // 獲取資源狀態(tài)
        stage = getNextStage(Stage.INITIALIZE);
        // 根據(jù)資源狀態(tài)獲取資源執(zhí)行器
        currentGenerator = getNextGenerator();
        //(4.2)執(zhí)行
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }

  /*DecodeJob*/
  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE
            : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE
            : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

  /*DecodeJob*/
  private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }

runReason 的默認(rèn)值為 INITIALIZE妥色,所以走的是第一個 case搪花。由于我們配置了禁用內(nèi)存與磁盤緩存,所以關(guān)注點(4.1)中得到的 stage 為 SOURCE嘹害,currentGenerator 為 SourceGenerator撮竿。拿到資源執(zhí)行器接著就是執(zhí)行了,點擊關(guān)注點(4.2)的 runGenerators() 方法進(jìn)去看看:

  /*DecodeJob*/
  private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    //(1)startNext()
    while (!isCancelled
        && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();

      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
  }

因為 currentGenerator 在這里為 SourceGenerator笔呀,所以調(diào)用 startNext() 方法的時候?qū)嶋H調(diào)用的是 SourceGenerator#startNext()幢踏。該方法執(zhí)行完返回的結(jié)果為 true,所以 while 循環(huán)是進(jìn)不去的许师。

那么我們接下來看下 SourceGenerator#startNext():

  /*SourceGenerator*/
  @Override
  public boolean startNext() {

    ...

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      //(1)
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
              || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        //(2)
        startNextLoad(loadData);
      }
    }
    return started;
  }

這里我標(biāo)記了 2 個關(guān)注房蝉,分別如下:

  • SourceGenerator#startNext() 中的關(guān)注點(1)
    通過 helper#getLoadData() 獲取 LoadData 集合僚匆,實際集合里面也就只有一個元素,然后通過索引獲取一個 LoadData搭幻,看看里面是怎么獲取 LoadData 集合的:
  /*DecodeHelper*/
  List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
      //noinspection ForLoopReplaceableByForEach to improve perf
      for (int i = 0, size = modelLoaders.size(); i < size; i++) {
        ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
        // 關(guān)注點
        LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
        if (current != null) {
          loadData.add(current);
        }
      }
    }
    return loadData;
  }

可以看到咧擂,這里是通過 ModelLoader 對象的 buildLoadData() 方法獲取的 LoadData。由于是從網(wǎng)絡(luò)加載數(shù)據(jù)檀蹋,所以這里實際調(diào)用的是 HttpGlideUrlLoader#buildLoadData()松申,點進(jìn)去看看:

  /*HttpGlideUrlLoader*/
  @Override
  public LoadData<InputStream> buildLoadData(
      @NonNull GlideUrl model, int width, int height, @NonNull Options options) {
    // GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
    // spent parsing urls.
    GlideUrl url = model;
    if (modelCache != null) {
      url = modelCache.get(model, 0, 0);
      if (url == null) {
        modelCache.put(model, 0, 0, model);
        url = model;
      }
    }
    int timeout = options.get(TIMEOUT);
    // 關(guān)注點
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }

緩存相關(guān)的不看,看到最后一行實例化 LoadData 的時候順帶實例化了 HttpUrlFetcher俯逾,這個后面會用到贸桶。

  • SourceGenerator#startNext() 中的關(guān)注點(2)
    這里表示開始加載了,點進(jìn)去看看:
  /*SourceGenerator*/
  private void startNextLoad(final LoadData<?> toStart) {
    loadData.fetcher.loadData(
        helper.getPriority(),
        new DataCallback<Object>() {
          @Override
          public void onDataReady(@Nullable Object data) {
            if (isCurrentRequest(toStart)) {
              onDataReadyInternal(toStart, data);
            }
          }

          @Override
          public void onLoadFailed(@NonNull Exception e) {
            if (isCurrentRequest(toStart)) {
              onLoadFailedInternal(toStart, e);
            }
          }
        });
  }

這里的 loadData.fetcher 就是剛剛實例化 LoadData 的時候傳進(jìn)來的 HttpUrlFetcher桌肴,所以這里調(diào)用的是 HttpUrlFetcher#loadData():

  /*HttpUrlFetcher*/
  @Override
  public void loadData(
      @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      //(1)通過重定向加載數(shù)據(jù)
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      //(2)加載成功皇筛,回調(diào)數(shù)據(jù)
      callback.onDataReady(result);
    } catch (IOException e) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Failed to load data for url", e);
      }
      callback.onLoadFailed(e);
    } finally {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }

這里標(biāo)記了 2 個關(guān)注點,分別如下:

  • HttpUrlFetcher#loadData() 中的關(guān)注點(1)
    這里加載完數(shù)據(jù)后返回了 InputStream识脆,看看內(nèi)部是怎么實現(xiàn)的:
  /*HttpUrlFetcher*/
  private InputStream loadDataWithRedirects(
      URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
    
    ...

    // 獲取 HttpURLConnection 實例
    urlConnection = connectionFactory.build(url);
    for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
      urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
    }
    urlConnection.setConnectTimeout(timeout);
    urlConnection.setReadTimeout(timeout);
    urlConnection.setUseCaches(false);
    urlConnection.setDoInput(true);

    // Stop the urlConnection instance of HttpUrlConnection from following redirects so that
    // redirects will be handled by recursive calls to this method, loadDataWithRedirects.
    urlConnection.setInstanceFollowRedirects(false);

    // Connect explicitly to avoid errors in decoders if connection fails.
    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)) { // 請求成功
      // 獲取 InputStream
      return getStreamForSuccessfulRequest(urlConnection);
    } else if (isHttpRedirect(statusCode)) { // 重定向請求
      String redirectUrlString = urlConnection.getHeaderField("Location");
      if (TextUtils.isEmpty(redirectUrlString)) {
        throw new HttpException("Received empty or null redirect url");
      }
      URL redirectUrl = new URL(url, redirectUrlString);
      // Closing the stream specifically is required to avoid leaking ResponseBodys in addition
      // to disconnecting the url connection below. See #2352.
      cleanup();
      return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
    } else if (statusCode == INVALID_STATUS_CODE) {
      throw new HttpException(statusCode);
    } else {
      throw new HttpException(urlConnection.getResponseMessage(), statusCode);
    }
  }

  /*HttpUrlFetcher*/
  private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
      throws IOException {
    if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
      int contentLength = urlConnection.getContentLength();
      stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
    } else {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
      }
      stream = urlConnection.getInputStream();
    }
    return stream;
  }

可以看到设联,原來 Glide 底層是采用 HttpURLConnection 來進(jìn)行網(wǎng)絡(luò)請求的,請求成功后返回了 InputStream灼捂。

  • HttpUrlFetcher#loadData() 中的關(guān)注點(2)
    現(xiàn)在 InputStream 已經(jīng)拿到了离例,回去關(guān)注點(2)中看下是怎么將 InputStream 回調(diào)出去的。
    關(guān)注點(2)調(diào)用 callback.onDataReady(result) 會走如下一系列調(diào)用:
    /*MultiModelLoader*/
    @Override
    public void onDataReady(@Nullable Data data) {
      if (data != null) {
        // 執(zhí)行這里
        callback.onDataReady(data);
      } else {
        startNextOrFail();
      }
    }
  
  /*SourceGenerator*/
  private void startNextLoad(final LoadData<?> toStart) {
    loadData.fetcher.loadData(
        helper.getPriority(),
        new DataCallback<Object>() {
          @Override
          public void onDataReady(@Nullable Object data) {
            if (isCurrentRequest(toStart)) {
              // 執(zhí)行這里
              onDataReadyInternal(toStart, data);
            }
          }

          @Override
          public void onLoadFailed(@NonNull Exception e) {
            if (isCurrentRequest(toStart)) {
              onLoadFailedInternal(toStart, e);
            }
          }
        });
  }

  /*SourceGenerator*/
  void onDataReadyInternal(LoadData<?> loadData, Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      // We might be being called back on someone else's thread. Before doing anything, we should
      // reschedule to get back onto Glide's thread.
      cb.reschedule();
    } else {
      // 執(zhí)行這里
      cb.onDataFetcherReady(
          loadData.sourceKey,
          data,
          loadData.fetcher,
          loadData.fetcher.getDataSource(),
          originalKey);
    }
  }

  /*DecodeJob*/
  @Override
  public void onDataFetcherReady(
      Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
    this.currentSourceKey = sourceKey;
    this.currentData = data;
    this.currentFetcher = fetcher;
    this.currentDataSource = dataSource;
    this.currentAttemptingKey = attemptedKey;
    if (Thread.currentThread() != currentThread) {
      runReason = RunReason.DECODE_DATA;
      callback.reschedule(this);
    } else {
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        // (1)解碼
        decodeFromRetrievedData();
      } finally {
        GlideTrace.endSection();
      }
    }
  }

可以看到悉稠,經(jīng)過一系列調(diào)用宫蛆,最終走到關(guān)注點(1),這里就開始解碼數(shù)據(jù)了的猛,點進(jìn)去看看:

  /*DecodeJob*/
  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      //(1)解碼
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      //(2)解碼完成耀盗,通知下去
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }

這里標(biāo)記了 2 個關(guān)注點,分別如下:

  • DecodeJob#decodeFromRetrievedData() 中的關(guān)注點(1)
    點擊關(guān)注點(1)進(jìn)去看看:
  /*DecodeJob*/
  private <Data> Resource<R> decodeFromData(
      DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
    try {
      if (data == null) {
        return null;
      }
      long startTime = LogTime.getLogTime();
      Resource<R> result = decodeFromFetcher(data, dataSource);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Decoded result " + result, startTime);
      }
      return result;
    } finally {
      fetcher.cleanup();
    }
  }

  /*DecodeJob*/
  private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    // 獲取解碼器卦尊,解碼器里面封裝了 DecodePath叛拷,它就是用來解碼轉(zhuǎn)碼的
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    // 通過解碼器解析數(shù)據(jù)
    return runLoadPath(data, dataSource, path);
  }

  /*DecodeJob*/
  private <Data, ResourceType> Resource<R> runLoadPath(
      Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)
      throws GlideException {
    Options options = getOptionsWithHardwareConfig(dataSource);
    DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
    try {
      // ResourceType in DecodeCallback below is required for compilation to work with gradle.
      // 將解碼任務(wù)傳遞給 LoadPath 完成
      return path.load(
          rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
  }

  /*LoadPath*/
  public Resource<Transcode> load(
      DataRewinder<Data> rewinder,
      @NonNull Options options,
      int width,
      int height,
      DecodePath.DecodeCallback<ResourceType> decodeCallback)
      throws GlideException {
    List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
    try {
      return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
    } finally {
      listPool.release(throwables);
    }
  }

  /*LoadPath*/
  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;
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
      try {
        // 開始解析數(shù)據(jù)
        result = path.decode(rewinder, width, height, options, decodeCallback);
      } catch (GlideException e) {
        exceptions.add(e);
      }
      if (result != null) {
        break;
      }
    }

    if (result == null) {
      throw new GlideException(failureMessage, new ArrayList<>(exceptions));
    }

    return result;
  }

可以看到,上面主要是獲取解碼器(LoadPath)岂却,然后解碼器里面是封裝了 DecodePath忿薇,經(jīng)過一系列調(diào)用,最后其實是交給 DecodePath 的 decode() 方法來真正開始解析數(shù)據(jù)的躏哩。

點擊 decode() 方法進(jìn)去看看:

  /*DecodePath*/
  public Resource<Transcode> decode(
      DataRewinder<DataType> rewinder,
      int width,
      int height,
      @NonNull Options options,
      DecodeCallback<ResourceType> callback)
      throws GlideException {
    //(1)
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    //(2)
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    //(3)
    return transcoder.transcode(transformed, options);
  }

這里我標(biāo)記了 3 個關(guān)注點署浩,分別如下:

  • DecodePath#decode() 中的關(guān)注點(1)
    這里是一個解碼過程,主要是將原始數(shù)據(jù)解碼成原始圖片扫尺。點進(jìn)去看看:
  /*DecodePath*/
  private Resource<ResourceType> decodeResource(
      DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options)
      throws GlideException {
    List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());
    try {
      return decodeResourceWithList(rewinder, width, height, options, exceptions);
    } finally {
      listPool.release(exceptions);
    }
  }

  /*DecodePath*/
  private Resource<ResourceType> decodeResourceWithList(
      DataRewinder<DataType> rewinder,
      int width,
      int height,
      @NonNull Options options,
      List<Throwable> exceptions)
      throws GlideException {
    Resource<ResourceType> result = null;
    //noinspection ForLoopReplaceableByForEach to improve perf
    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();
          // 關(guān)注點
          result = decoder.decode(data, width, height, options);
        }
        // Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but
        // instead log and continue. See #2406 for an example.
      } catch (IOException | RuntimeException | OutOfMemoryError e) {

      ...

    }

    ...

    return result;
  }

可以看到筋栋,這里遍歷拿到可以解碼當(dāng)前數(shù)據(jù)的資源解碼器,然后調(diào)用 decode() 方法進(jìn)行解碼正驻。因為當(dāng)前數(shù)據(jù)是 InputStream弊攘,所以這里遍歷拿到的 ResourceDecoder 其實是 StreamBitmapDecoder抢腐,所以調(diào)用的是 StreamBitmapDecoder#decode()。

繼續(xù)看 StreamBitmapDecoder#decode():

  /*StreamBitmapDecoder*/
  @Override
  public Resource<Bitmap> decode(
      @NonNull InputStream source, int width, int height, @NonNull Options options)
      throws IOException {

    // Use to fix the mark limit to avoid allocating buffers that fit entire images.
    final RecyclableBufferedInputStream bufferedStream;
    final boolean ownsBufferedStream;
    if (source instanceof RecyclableBufferedInputStream) {
      bufferedStream = (RecyclableBufferedInputStream) source;
      ownsBufferedStream = false;
    } else {
      bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
      ownsBufferedStream = true;
    }

    // Use to retrieve exceptions thrown while reading.
    // TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a
    // way to determine if a Bitmap is partially decoded, consider removing.
    ExceptionCatchingInputStream exceptionStream =
        ExceptionCatchingInputStream.obtain(bufferedStream);

    // Use to read data.
    // Ensures that we can always reset after reading an image header so that we can still
    // attempt to decode the full image even when the header decode fails and/or overflows our read
    // buffer. See #283.
    MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
    UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
    try {
      // (1)
      return downsampler.decode(invalidatingStream, width, height, options, callbacks);
    } finally {
      exceptionStream.release();
      if (ownsBufferedStream) {
        bufferedStream.release();
      }
    }
  }

  /*Downsampler*/
  public Resource<Bitmap> decode(...)
      throws IOException {
    return decode(...);
  }

  /*Downsampler*/
  private Resource<Bitmap> decode(...)
      throws IOException {

    ...

    try {
      // (2)根據(jù)輸入流解碼得到 Bitmap
      Bitmap result =
          decodeFromWrappedStreams(
              imageReader,
              bitmapFactoryOptions,
              downsampleStrategy,
              decodeFormat,
              preferredColorSpace,
              isHardwareConfigAllowed,
              requestedWidth,
              requestedHeight,
              fixBitmapToRequestedDimensions,
              callbacks);
      // (3)將 Bitmap 包裝成 Resource<Bitmap> 返回
      return BitmapResource.obtain(result, bitmapPool);
    } finally {
      releaseOptions(bitmapFactoryOptions);
      byteArrayPool.put(bytesForOptions);
    }
  }

可以看到肴颊,關(guān)注點(1)中調(diào)用了 Downsampler#decode()氓栈,然后走到關(guān)注點(2)將輸入流解碼得到 Bitmap渣磷,最后將 Bitmap 包裝成 Resource<Bitmap> 返回婿着。關(guān)注點(2)中其實就是使用 BitmapFactory 根據(jù) exif 方向?qū)D像進(jìn)行下采樣,解碼和旋轉(zhuǎn)醋界,最后調(diào)用原生的 BitmapFactory#decodeStream() 得到的 Bitmap竟宋,里面的細(xì)節(jié)就不展開了。

  • DecodePath#decode() 中的關(guān)注點(2)
    這里會調(diào)用 callback#onResourceDecoded() 進(jìn)行回調(diào)形纺,這里是回調(diào)到 DecodeJob#onResourceDecoded():
    /*DecodeJob*/
    @Override
    public Resource<Z> onResourceDecoded(@NonNull Resource<Z> decoded) {
      return DecodeJob.this.onResourceDecoded(dataSource, decoded);
    }

繼續(xù)跟進(jìn):

  /*DecodeJob*/
  <Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {
    @SuppressWarnings("unchecked")
    Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
    Transformation<Z> appliedTransformation = null;
    Resource<Z> transformed = decoded;
    // 如果不是從磁盤緩存中獲取的丘侠,則需要對資源進(jìn)行轉(zhuǎn)換
    if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
      appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
      transformed = appliedTransformation.transform(glideContext, decoded, width, height);
    }
    // TODO: Make this the responsibility of the Transformation.
    if (!decoded.equals(transformed)) {
      decoded.recycle();
    }

    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;

    // 緩存相關(guān)
    ...

    return result;
  }

可以看到,這里的任務(wù)是對資源進(jìn)行轉(zhuǎn)換逐样,也就是我們請求的時候如果配置了 centerCrop蜗字、fitCenter 等,這里就需要轉(zhuǎn)換成對應(yīng)的資源脂新。

  • DecodePath#decode() 中的關(guān)注點(3)
    關(guān)注點(3) transcoder.transcode(transformed, options) 中的 transcoder 為 BitmapDrawableTranscoder挪捕,所以這里調(diào)用的是 BitmapDrawableTranscoder#transcode():
  /*BitmapDrawableTranscoder*/
  @Override
  public Resource<BitmapDrawable> transcode(
      @NonNull Resource<Bitmap> toTranscode, @NonNull Options options) {
    return LazyBitmapDrawableResource.obtain(resources, toTranscode);
  }

繼續(xù)往下跟:

  /*LazyBitmapDrawableResource*/
  public static Resource<BitmapDrawable> obtain(
      @NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
    if (bitmapResource == null) {
      return null;
    }
    return new LazyBitmapDrawableResource(resources, bitmapResource);
  }

  /*LazyBitmapDrawableResource*/
  private LazyBitmapDrawableResource(
      @NonNull Resources resources, @NonNull Resource<Bitmap> bitmapResource) {
    this.resources = Preconditions.checkNotNull(resources);
    this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
  }

因為 LazyBitmapDrawableResource 實現(xiàn)了 Resource<BitmapDrawable>,所以最后是返回了一個 Resource<BitmapDrawable>争便,也就是說轉(zhuǎn)碼這一步是將 Resource<Bitmap> 轉(zhuǎn)成了 Resource<BitmapDrawable>级零。

到這里,DecodeJob#decodeFromRetrievedData() 中的關(guān)注點(1)解碼的流程就走完了滞乙,我們回去繼續(xù)看關(guān)注點(2)奏纪。

  • DecodeJob#decodeFromRetrievedData() 中的關(guān)注點(2)
    這里我再貼一下之前的代碼:
  /*DecodeJob*/
  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      //(1)解碼
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      //(2)解碼完成,通知下去
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }

剛剛關(guān)注點(1)的解碼過程已經(jīng)完成斩启,現(xiàn)在需要通知下去了序调,點擊 notifyEncodeAndRelease() 方法進(jìn)去看看:

  /*DecodeJob*/
  private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    if (resource instanceof Initializable) {
      ((Initializable) resource).initialize();
    }

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

    // (1)通知外面已經(jīng)完成了
    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      if (deferredEncodeManager.hasResourceToEncode()) {
        // 將資源緩存到磁盤
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }
    // Call onEncodeComplete outside the finally block so that it's not called if the encode process
    // throws.
    // 完成,釋放各種資源
    onEncodeComplete();
  }

點擊關(guān)注點(1)進(jìn)去看看:

  /*DecodeJob*/
  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
  }

這里的 callback 只有一個實現(xiàn)類就是 EngineJob兔簇,所以直接看 EngineJob#onResourceReady():

  /*EngineJob*/
  @Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    synchronized (this) {
      this.resource = resource;
      this.dataSource = dataSource;
    }
    // 通知結(jié)果回調(diào)
    notifyCallbacksOfResult();
  }

繼續(xù)跟進(jìn):

  /*EngineJob*/
  void notifyCallbacksOfResult() {
    ResourceCallbacksAndExecutors copy;
    Key localKey;
    EngineResource<?> localResource;
    synchronized (this) {
      stateVerifier.throwIfRecycled();
      // 如果取消发绢,則回收和釋放資源
      if (isCancelled) {
        resource.recycle();
        release();
        return;
      } else if (cbs.isEmpty()) {
        throw new IllegalStateException("Received a resource without any callbacks to notify");
      } else if (hasResource) {
        throw new IllegalStateException("Already have resource");
      }
      engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);

      localKey = key;
      localResource = engineResource;
    }

    //(1)
    engineJobListener.onEngineJobComplete(this, localKey, localResource);

    //(2)
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    decrementPendingCallbacks();
  }

這里標(biāo)記了 2 個關(guān)注點,分別如下:

  • EngineJob#notifyCallbacksOfResult() 中的關(guān)注點(1)
    這里表示 EngineJob 完成了男韧,回調(diào)給 Engine朴摊,進(jìn)入 onEngineJobComplete() 看看:
  /*Engine*/
  @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 && resource.isMemoryCacheable()) {
      activeResources.activate(key, resource);
    }

    jobs.removeIfCurrent(key, engineJob);
  }

  /*ActiveResources*/
  synchronized void activate(Key key, EngineResource<?> resource) {
    ResourceWeakReference toPut =
        new ResourceWeakReference(
            key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);

    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
    if (removed != null) {
      removed.reset();
    }
  }

這里主要判斷是否配置了內(nèi)存緩存,有的話就存到活動緩存中此虑。

  • EngineJob#notifyCallbacksOfResult() 中的關(guān)注點(2)
    我這里再貼一下 notifyCallbacksOfResult() 方法中與關(guān)注點(2)有關(guān)的代碼:
  /*EngineJob*/
  void notifyCallbacksOfResult() {
    ResourceCallbacksAndExecutors copy;
    synchronized (this) {
      copy = cbs.copy();
    }
    //(2)
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
  }

可以看到甚纲,這里遍歷 copy 后拿到了線程池的執(zhí)行器(entry.executor),copy 是上面一行 copy = cbs.copy(); 得到的朦前,我們利用反推看下這個線程池的執(zhí)行器是怎么來的:

    // 1. cbs.copy()
    /*ResourceCallbacksAndExecutors*/
    ResourceCallbacksAndExecutors copy() {
      return new ResourceCallbacksAndExecutors(new ArrayList<>(callbacksAndExecutors));
    }

    // 2. callbacksAndExecutors 集合是哪里添加元素的
    /*ResourceCallbacksAndExecutors*/
    void add(ResourceCallback cb, Executor executor) {
      callbacksAndExecutors.add(new ResourceCallbackAndExecutor(cb, executor));
    }

  // 3. 上面的 add() 方法是哪里調(diào)用的
  /*EngineJob*/
  synchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) {
    cbs.add(cb, callbackExecutor);
  }

  // 4. 上面的 addCallback() 方法是哪里調(diào)用的
  /*Engine*/
  private <R> LoadStatus waitForExistingOrStartNewJob(
      ...
      Executor callbackExecutor,
      ...
      ) {

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

這里我們反推了 4 步介杆,發(fā)現(xiàn)是從 waitForExistingOrStartNewJob() 方法那里傳進(jìn)來的鹃操,繼續(xù)往后推的代碼就不列出來了,最后發(fā)現(xiàn)是從 into() 方法哪里傳過來的(可以自己正序再跟蹤一遍源碼):

  /*RequestBuilder*/
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {

    ...

    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

也就是這里的 Executors.mainThreadExecutor()春哨,點進(jìn)去看看是什么:

public final class Executors {

  private static final Executor MAIN_THREAD_EXECUTOR =
      new Executor() {
        private final Handler handler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(@NonNull Runnable command) {
          handler.post(command);
        }
      };

  /** Posts executions to the main thread. */
  public static Executor mainThreadExecutor() {
    return MAIN_THREAD_EXECUTOR;
  }
}

原來這里是創(chuàng)建了一個 Executor 的實例荆隘,然后重寫了 execute() 方法,但是里面卻用的是主線程中的 Handler 來 post() 一個任務(wù)赴背,也就是切換到了主線程去執(zhí)行了椰拒。

再回到關(guān)注點(2),發(fā)現(xiàn)執(zhí)行這一句 entry.executor.execute(new CallResourceReady(entry.cb)) 實際執(zhí)行的是 handler.post(command)凰荚,所以 CallResourceReady 中的 run() 方法是在主線程中執(zhí)行的燃观。真是想不到呀阱高,原來子線程切換到主線程在一開始的 into() 方法中就做了鋪墊衫生!

那么我們繼續(xù)往下看侄旬,進(jìn)入 CallResourceReady#run():

  /*EngineJob*/
  private class CallResourceReady implements Runnable {

    @Override
    public void run() {
      synchronized (cb.getLock()) {
        synchronized (EngineJob.this) {
          if (cbs.contains(cb)) {
            engineResource.acquire();
            // 關(guān)注點
            callCallbackOnResourceReady(cb);
            removeCallback(cb);
          }
          decrementPendingCallbacks();
        }
      }
    }
  }

繼續(xù)跟進(jìn):

  /*EngineJob*/
  void callCallbackOnResourceReady(ResourceCallback cb) {
    try {
      // 資源準(zhǔn)備好了绿满,回調(diào)給上一層
      cb.onResourceReady(engineResource, dataSource);
    } catch (Throwable t) {
      throw new CallbackException(t);
    }
  }

上面會回調(diào)到 SingleRequest#onResourceReady()员串,進(jìn)去看看:

  /*SingleRequest*/
  @Override
  public void onResourceReady(Resource<?> resource, DataSource dataSource) {
    stateVerifier.throwIfRecycled();
    Resource<?> toRelease = null;
    try {
      synchronized (requestLock) {
        loadStatus = null;
        if (resource == null) {
          GlideException exception =
              new GlideException(...);
          onLoadFailed(exception);
          return;
        }

        Object received = resource.get();
        if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
          toRelease = resource;
          this.resource = null;
          GlideException exception =
              new GlideException(...);
          onLoadFailed(exception);
          return;
        }

        if (!canSetResource()) {
          toRelease = resource;
          this.resource = null;
          status = Status.COMPLETE;
          return;
        }

        // 關(guān)注點
        onResourceReady((Resource<R>) resource, (R) received, dataSource);
      }
    } finally {
      if (toRelease != null) {
        engine.release(toRelease);
      }
    }
  }

可以看到吭产,前面是一些加載失敗的判斷润樱,看下最后的 onResourceReady():

  /*SingleRequest*/
  private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {

    ...

    try {
      boolean anyListenerHandledUpdatingTarget = false;
      if (requestListeners != null) {
        for (RequestListener<R> listener : requestListeners) {
          anyListenerHandledUpdatingTarget |=
              listener.onResourceReady(result, model, target, dataSource, isFirstResource);
        }
      }
      anyListenerHandledUpdatingTarget |=
          targetListener != null
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);

      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
        // 回調(diào)給 ImageViewTarget
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }

    // 通知加載成功
    notifyLoadSuccess();
  }

這里的 target 為 ImageViewTarget单雾,進(jìn)入 ImageViewTarget#onResourceReady() 看看:

  /*ImageViewTarget*/
  @Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
  }

  /*ImageViewTarget*/
  private void setResourceInternal(@Nullable Z resource) {
    setResource(resource);
    maybeUpdateAnimatable(resource);
  }

因為在前面 "2.3.1 GlideContext#buildImageViewTarget()" 中創(chuàng)建的 ImageViewTarget 的實現(xiàn)類是 DrawableImageViewTarget践啄,所以這里調(diào)用的是 DrawableImageViewTarget#setResource()浇雹,點進(jìn)去看看:

  /*DrawableImageViewTarget*/
  @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }

到這里終于將圖片顯示到我們的 ImageView 中了!into() 方法也結(jié)束了往核。

2.3.4 小結(jié)

要我總結(jié)的話箫爷,只能說 into() 方法實在是太復(fù)雜了,with() 與 load() 方法在它面前就是小嘍嘍聂儒。
into() 方法主要做了發(fā)起網(wǎng)絡(luò)請求虎锚、緩存數(shù)據(jù)、解碼并顯示圖片衩婚。

大概流程為:
發(fā)起網(wǎng)絡(luò)請求前先判斷是否有內(nèi)存緩存窜护,有則直接從內(nèi)存緩存那里獲取數(shù)據(jù)進(jìn)行顯示,沒有則判斷是否有磁盤緩存非春;有磁盤緩存則直接從磁盤緩存那里獲取數(shù)據(jù)進(jìn)行顯示柱徙,沒有才發(fā)起網(wǎng)絡(luò)請求;網(wǎng)絡(luò)請求成功后將返回的數(shù)據(jù)存儲到內(nèi)存與磁盤緩存(如果有配置)奇昙,最后將返回的輸入流解碼成 Drawable 顯示在 ImageView 上护侮。

三、總結(jié)

Glide 源碼相對于我之前寫的其他源碼文章來說確實難啃储耐,但是耐心看完發(fā)現(xiàn)收獲也很多羊初。例如利用一個隱藏的 Fragment 來管理 Glide 請求的生命周期,利用四級緩存來提升加載圖片的效率,還有網(wǎng)絡(luò)請求成功后是在哪里切換到主線程的等等长赞。

參考資料:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晦攒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子得哆,更是在濱河造成了極大的恐慌脯颜,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贩据,死亡現(xiàn)場離奇詭異栋操,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)乐设,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門讼庇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人近尚,你說我怎么就攤上這事〕∏冢” “怎么了戈锻?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長和媳。 經(jīng)常有香客問我格遭,道長,這世上最難降的妖魔是什么留瞳? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任拒迅,我火速辦了婚禮,結(jié)果婚禮上她倘,老公的妹妹穿的比我還像新娘璧微。我一直安慰自己,他們只是感情好硬梁,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布前硫。 她就那樣靜靜地躺著,像睡著了一般荧止。 火紅的嫁衣襯著肌膚如雪屹电。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天跃巡,我揣著相機(jī)與錄音危号,去河邊找鬼。 笑死素邪,一個胖子當(dāng)著我的面吹牛外莲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播娘香,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼苍狰,長吁一口氣:“原來是場噩夢啊……” “哼办龄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起淋昭,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤俐填,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后翔忽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體英融,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年歇式,在試婚紗的時候發(fā)現(xiàn)自己被綠了驶悟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡材失,死狀恐怖痕鳍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情龙巨,我是刑警寧澤笼呆,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站旨别,受9級特大地震影響诗赌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜秸弛,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一铭若、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧递览,春花似錦叼屠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至憎兽,卻和暖如春冷离,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纯命。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工西剥, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亿汞。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓瞭空,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子咆畏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355