1. 程式人生 > >Glide原始碼分析(一)從用法來看之with方法

Glide原始碼分析(一)從用法來看之with方法

繼續啃原始碼,用過Glide的人,肯定都覺得它好好用,我們一般只需要幾行程式碼,就可以達到我們想要的效果,可以在這個背後是什麼呢?就需要我們來看了。

我一般看原始碼,我喜歡先從用法來看,然後一步一步的再細扣,所以就先從用法來看Glide的整體流程。

用過Glide的人,用下面這段程式碼,就可以進行圖片的載入:

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

先就這三步,我們慢慢來分析:

with()

在Glide中,有很多with方法的過載:

 @NonNull
  public
static RequestManager with(@NonNull Context context) { return getRetriever(context).get(context); } @NonNull public static RequestManager with(@NonNull Activity activity) { return getRetriever(activity).get(activity); } /** * Begin a load with Glide that will tied to the give * {@link android.support.v4.app.FragmentActivity}'s lifecycle and that uses the given * {@link android.support.v4.app.FragmentActivity}'s default options. * * @param activity The activity to use. * @return A RequestManager for the given FragmentActivity that can be used to start a load. */
@NonNull public static RequestManager with(@NonNull FragmentActivity activity) { return getRetriever(activity).get(activity); } @NonNull public static RequestManager with(@NonNull Fragment fragment) { return getRetriever(fragment.getActivity()).get(fragment); } @SuppressWarnings
("deprecation") @Deprecated @NonNull public static RequestManager with(@NonNull android.app.Fragment fragment) { return getRetriever(fragment.getActivity()).get(fragment); } @NonNull public static RequestManager with(@NonNull View view) { return getRetriever(view.getContext()).get(view); }

上面就是with方法的所有的過載方法,雖然方法很多,但是本質都是一樣的,因為getRetriever(Context context)方法,它的引數都是Context,雖然with方法的過載很多,可是最終呼叫的都是``getRetriever(Context context).get()`

通過官方對它的描述,我們可以總結出,with方法的主要作用是和Context,或Activity,或FragmentActivity,或Fragment,或View進行生命週期的繫結。

繼續看:
先看getRetriever(activity)

@NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    //進行null檢查
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");
    return Glide.get(context).getRequestManagerRetriever();
  }
 /**
   * Get the singleton.
   *得到Glide的單例
   * @return the singleton
   */
  @NonNull
  public static Glide get(@NonNull Context context) {
  //這裡用的是double-chcke的單例模式
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }

    return glide;
  }


private static void checkAndInitializeGlide(@NonNull Context context) {
    // In the thread running initGlide(), one or more classes may call Glide.get(context).
    // Without this check, those calls could trigger infinite recursion.
    if (isInitializing) {
      throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"
          + " use the provided Glide instance instead");
    }
    isInitializing = true;
    initializeGlide(context);
    isInitializing = false;
  }

 private static void initializeGlide(@NonNull Context context) {
    initializeGlide(context, new GlideBuilder());
  }

  @SuppressWarnings("deprecation")
  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
  //得到ApplicationContext
    Context applicationContext = context.getApplicationContext();
    //獲取應用中帶註解的GlideModule(annotationGeneratedModule),
    //GlideModule:使用者自定義Glide配置模組,用來修改預設的Glide配置資訊
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      manifestModules = new ManifestParser(applicationContext).parse();
    }
	//移除被過濾的GlideModule
    if (annotationGeneratedModule != null
        && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
      Set<Class<?>> excludedModuleClasses =
          annotationGeneratedModule.getExcludedModuleClasses();
      Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
      while (iterator.hasNext()) {
        com.bumptech.glide.module.GlideModule current = iterator.next();
        if (!excludedModuleClasses.contains(current.getClass())) {
          continue;
        }
        if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
        }
        iterator.remove();
      }
    }

    if (Log.isLoggable(TAG, Log.DEBUG)) {
      for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {
        Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());
      }
    }

    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    builder.setRequestManagerFactory(factory);
    
    //逐個回撥使用者配置的GlideModule
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    //回撥註解標記的AppGlideModule
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    //構建Glide例項
    Glide glide = builder.build(applicationContext);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.registerComponents(applicationContext, glide, glide.registry);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }

在上面程式碼中,Glide先載入配置,那它載入了那些配置呢?
在使用Glide的時候,我們都會有一些想要設定的系統級配置,如設定快取的儲存位置,快取區的大小,網路載入模組等,那麼我們通常就是使用GlideModule進行配置。在Glide 3.x中我們首先會定義一個繼承於GlideModule的類,然後在專案的AndroidMenifest.xml中進行指定:


public class GlideConfiguration implements GlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        //通過builder.setXXX進行配置.
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        //通過glide.register進行配置.
    }
}

<meta-data android:name="com.test.GlideConfiguration"
           android:value="GlideModule"/>

而在Glide4中,提供另外一個配置的模式,那就是註解,並且不再實現GlideModule,而是繼承AppGlideModule和LibraryGlideModule,分別對應Application和Libaray,使用@GlideModule註解進行標記。而Glide 3.x中的配置方法已經建議放棄使用。

@GlideModule
public class GlideConfiguration extends AppGlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        //設定快取到外部儲存器
        builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context)); 
    }
}

在上面程式碼中,有一句特別重要的程式碼,在回撥registerComponents前,首先構建了glide例項:

 Glide glide = builder.build(applicationContext);

我們就看看是怎麼構造Glide單例的把:

@NonNull
  Glide build(@NonNull Context context) {
  //建立資源載入器,實際上是一個執行緒池,
  //使用者載入URL或者本地資源,即載入源資料。
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }
	
	/**建立本地快取資源載入請求器,也是一個執行緒池,
	用於載入快取在磁碟中的資料
	並且這個執行緒池不能用於載入url網路資料
	*/
    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }
	
	//建立動畫載入請求器
    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }

	//記憶體計算器
    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }

	//網路連線狀態檢測器工程,
	//用於監聽網路狀態
    if (connectivityMonitorFactory == null) {
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }
	/**建立 bitmap資源快取池,
	該快取主要用於bitmap資源的快取和回收,
	避免由於大量建立和回收bitmap導致記憶體抖動
	*/
    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }

	//陣列資源快取池
    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }

	//記憶體快取,用於快取完成載入和顯示的圖片資料資源
    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }
	//本地磁碟快取器,預設為儲存在app內部私密目錄
    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
	//建立圖片載入引擎,
	//用於執行圖片載入請求驅動
    if (engine == null) {
      engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              GlideExecutor.newAnimationExecutor(),
              isActiveResourceRetentionAllowed);
    }
	//建立請求索引器
    RequestManagerRetriever requestManagerRetriever =
        new RequestManagerRetriever(requestManagerFactory);
	//最後建立glide
    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptions.lock(),
        defaultTransitionOptions);
  }

通過上面一系列工具的新建,Glide建立了資源請求執行緒池,本地快取載入執行緒池,動畫執行緒池,記憶體執行緒池,磁碟快取工具等,又構造了Engine資料載入引起,最後構建Glide:

Glide(
      @NonNull Context context,
      @NonNull Engine engine,
      @NonNull MemoryCache memoryCache,
      @NonNull BitmapPool bitmapPool,
      @NonNull ArrayPool arrayPool,
      @NonNull RequestManagerRetriever requestManagerRetriever,
      @NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
      int logLevel,
      @NonNull RequestOptions defaultRequestOptions,
      @NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {
      
      //賦值 GlideBuilder注入的工具’
    this.engine = engine;
    this.bitmapPool = bitmapPool;
    this.arrayPool = arrayPool;
    this.memoryCache = memoryCache;
    this.requestManagerRetriever = requestManagerRetriever;
    this.connectivityMonitorFactory = connectivityMonitorFactory;

    DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
    bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);

    final Resources resources = context.getResources();
    
	//新建註冊器
    registry = new Registry();
    registry.register(new DefaultImageHeaderParser());
    
	//構建解碼器和編碼器
    Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(),
        resources.getDisplayMetrics(), bitmapPool, arrayPool);
    ByteBufferGifDecoder byteBufferGifDecoder =
        new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool);
    ResourceDecoder<ParcelFileDescriptor, Bitmap> parcelFileDescriptorVideoDecoder =
        VideoDecoder.parcel(bitmapPool);
    ByteBufferBitmapDecoder byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
    StreamBitmapDecoder streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool);
    ResourceDrawableDecoder resourceDrawableDecoder =
        new ResourceDrawableDecoder(context);
    ResourceLoader.StreamFactory resourceLoaderStreamFactory =
        new ResourceLoader.StreamFactory(resources);
    ResourceLoader.UriFactory resourceLoaderUriFactory =
        new ResourceLoader.UriFactory(resources);
    ResourceLoader.FileDescriptorFactory resourceLoaderFileDescriptorFactory =
        new ResourceLoader.FileDescriptorFactory(resources);
    ResourceLoader.AssetFileDescriptorFactory resourceLoaderAssetFileDescriptorFactory =
        new ResourceLoader.AssetFileDescriptorFactory(resources);
    BitmapEncoder bitmapEncoder = new BitmapEncoder(arrayPool);

    BitmapBytesTranscoder bitmapBytesTranscoder = new BitmapBytesTranscoder();
    GifDrawableBytesTranscoder gifDrawableBytesTranscoder = new GifDrawableBytesTranscoder();

    ContentResolver contentResolver = context.getContentResolver();
	//開始註冊各個型別對應的編碼器和解碼器
    registry
        .append(ByteBuffer.class, new ByteBufferEncoder())
        .append(InputStream.class, new StreamEncoder(arrayPool))
        /* Bitmaps */
        .append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
        .append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)
        .append(
            Registry.BUCKET_BITMAP,
            ParcelFileDescriptor.class,
            Bitmap.class,
            parcelFileDescriptorVideoDecoder)
        .append(
            Registry.BUCKET_BITMAP,
            AssetFileDescriptor.class,
            Bitmap.class,
            VideoDecoder.asset(bitmapPool))
        .append(Bitmap.class, Bitmap.class, UnitModelLoader.Factory.<Bitmap>getInstance())
        .append(
            Registry.BUCKET_BITMAP, Bitmap.class, Bitmap.class, new UnitBitmapDecoder())
        .append(Bitmap.class, bitmapEncoder)
        /* BitmapDrawables */
        .append(
            Registry.BUCKET_BITMAP_DRAWABLE,
            ByteBuffer.class,
            BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
        .append(
            Registry.BUCKET_BITMAP_DRAWABLE,
            InputStream.class,
            BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
        .append(
            Registry.BUCKET_BITMAP_DRAWABLE,
            ParcelFileDescriptor.class,
            BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, parcelFileDescriptorVideoDecoder))
        .append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, bitmapEncoder))
        /* GIFs */
        .append(
            Registry.BUCKET_GIF,
            InputStream.class,
            GifDrawable.class,
            new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool))
        .append(Registry
            
           

相關推薦

Glide原始碼分析用法來看with方法

繼續啃原始碼,用過Glide的人,肯定都覺得它好好用,我們一般只需要幾行程式碼,就可以達到我們想要的效果,可以在這個背後是什麼呢?就需要我們來看了。 我一般看原始碼,我喜歡先從用法來看,然後一步一步的再細扣,所以就先從用法來看Glide的整體流程。 用過Glide的人,用下面這段

Glide原始碼分析——用法來看load&into方法

上一篇,我們分析了with方法,文章連結: https://blog.csdn.net/qq_36391075/article/details/82833260 在with方法中,進行了Glide的初始化,建立了RequesManger,並且綁定了生命週期,最終返回了一個Reques

Glide原始碼分析——DiskLruCache磁碟快取的實現

Glide磁碟的實現主要是通過DiskLruCache來實現的。DiskLruCache並非針對Glide編寫的,而是一個通用的磁碟快取實現,雖然並非Google官方的程式碼,但是已經在很多應用中得到了引入使用。 journal日誌 DiskLruCache

Mybatis原始碼分析6—— JDBC看Mybatis的設計

Java資料庫連線,(Java Database Connectivity,簡稱JDBC)是Java語言中用來規範客戶端程式如何來訪問資料庫的應用程式介面,提供了諸如查詢和更新資料庫中資料的方法。 六步流程: 載入驅動(5.x驅動包不需要這步了) 建立

Mybatis原始碼分析3—— Mybatis的視角去看Bean的初始化流程

不涉及Spring完整的啟動流程,僅僅從Mybatis的視角去分析幾個關鍵的方法,找到Mybatis是如何通過這幾個擴充套件點植入進去的,反過來看Spring是如何設計,埋下這些伏筆,實現其可擴充套件性。 springContext-mybatis.xml的配置: <!--

Flume NG原始碼分析基於靜態properties檔案的配置模組

日誌收集是網際網路公司的一個重要服務,Flume NG是Apache的頂級專案,是分散式日誌收集服務的一個開源實現,具有良好的擴充套件性,與其他很多開源元件可以無縫整合。搜了一圈發現介紹Flume NG的文章有不少,但是深入分析Flume NG原始碼的卻沒有。準備寫一個系列分析一下Flume NG的

GCC原始碼分析——介紹與安裝

原文連結:http://blog.csdn.net/sonicling/article/details/6702031     上半年一直在做有關GCC和LD的專案,到現在還沒做完。最近幾天程式設計的那臺電腦壞了,所以趁此間隙寫一點相關的分析和

zigbee ZStack-2.5.1a原始碼分析

先看main, 在檔案Zmain.c裡面 main osal_init_system(); osalInitTasks(); ... ... SampleApp_Init( taskID ); // 使用者定義的任務

Docker Client原始碼分析

主要內容: Docker Client在Docker中的定位,以及Docker Client原始碼的初步分析。 本文選取Docker拆分為DockerCE(社群版)和DockerEE(企業版)之後的Docker-CE的第一個穩定版本v17.06.0-ce。 https://github.com/docker

Hibernate使用及原始碼分析

Hibernate使用及原始碼分析(一) 本篇文章主要通過hibernate初級使用分析一下原始碼,只是給初學者一點小小的建議,不喜勿噴,謝謝! hibernate環境搭建 簡單使用 原始碼走讀 一 hibernate環境搭建 這裡直接

SpringCloud原始碼分析--客戶端搭建

一、前言 上一節的註冊中心搭建完成了,本節開始搭建客戶端,其實對於springcloud的Eureka註冊中心而言,他本身就是服務端也是客戶端,我們上節待見服務端註冊中心的時候,已經通過配置來設定其不向自己註冊,和不去檢索服務的功能,保持了其作為服務註冊中心的相對的功能單一性。 二、pom檔案

Vue原始碼分析:入口檔案

Vue原始碼分析(一):入口檔案   首先開啟命令列,從github下載原始碼,下載到自己的工作目錄。 git clone https://github.com/vuejs/vue.git   這裡我下載的是2.5.17版本的,vue 原始碼是由各種模組用 rollup 工具

okhttp原始碼分析——基本流程超詳細

1.okhttp原始碼分析(一)——基本流程(超詳細) 2.okhttp原始碼分析(二)——RetryAndFollowUpInterceptor過濾器 3.okhttp原始碼分析(三)——CacheInterceptor過濾器 4.okhttp原始碼分析(四)——Conn

spring事務管理原始碼分析配置和事務增強代理的生成流程

在本篇文章中,將會介紹如何在spring中進行事務管理,之後對其內部原理進行分析。主要涉及 @EnableTransactionManagement註解為我們做了什麼? 為什麼標註了@Transactional註解的方法就可以具有事務的特性,保持了資料的ACID特性?spring到底是如何具有這樣

Android系統播放器MediaPlayer原始碼分析

前言 對於MediaPlayer播放器的原始碼分析內容相對來說比較多,會從Java->JNI->C/C++慢慢分析,後面會慢慢更新。另外,部落格只作為自己學習記錄的一種方式,對於其他的不過多的評論。 MediaPlayerDemo public class MainA

Koa原始碼閱讀搭建Web伺服器說起

先複習一下使用原生 Node.js 搭建一個 Web 伺服器。 var http = require('http'); var server = http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'te

Android7.1 [Camera] Camera Hal 原始碼分析

原始碼平臺:rk3399   命令列ls看下原始碼的結構 hardware/rockchip/camera/CameraHal: lib目錄 原始碼的檔案看起來有點多,我們看看Android.mk檔案, 這些檔案最終編譯成camera.rk30bo

Cat原始碼分析:Client端

前言 cat的Client端所做的工作就是收集埋點資訊,將埋點資訊處理成messageTree,放到傳送佇列中,在啟動另一個執行緒,非同步消費佇列,進行訊息的傳送。 本文涉及到三個內容: 客戶端初始化:做了哪些準備工作 message的建立過程 客戶端的傳

laravel框架原始碼分析自動載入

一、前言   使用php已有好幾年,laravel的使用也是有好長時間,但是一直對於框架原始碼的理解不深,原因很多,歸根到底還是php基礎不紮實,所以原始碼看起來也比較吃力。最近有時間,所以開啟第5、6遍的框架原始碼探索之旅,前面幾次都是看了一些就放棄,希望這次能夠看完。每一次看原始碼都會有新的收穫,因為框

github上hamsternz/FPGA_DisplayPort 的VHDL原始碼分析

原始碼來源於https://github.com/hamsternz/FPGA_DisplayPort。由於我也是第一次接觸這個介面,所以文中肯定有我理解錯誤的地方,懇請指正。要看懂程式碼首先還是要對協議有一定了解。所以我做的原始碼分析中會和協議結合起來。 激勵檔案test_source_800