2018-10-09 glide

關(guān)于Glide

Glide是一個(gè)快速高效的Android圖片加載庫(kù),注重于平滑的滾動(dòng)。Glide提供了易用的API,高性能、可擴(kuò)展的圖片解碼管道(decode pipeline),以及自動(dòng)的資源池技術(shù)。



Glide 支持拉取,解碼和展示視頻快照,圖片,和GIF動(dòng)畫。Glide的Api是如此的靈活,開發(fā)者甚至可以插入和替換成自己喜愛的任何網(wǎng)絡(luò)棧。默認(rèn)情況下,Glide使用的是一個(gè)定制化的基于HttpUrlConnection的棧,但同時(shí)也提供了與Google Volley和Square OkHttp快速集成的工具庫(kù)。
雖然Glide 的主要目標(biāo)是讓任何形式的圖片列表的滾動(dòng)盡可能地變得更快、更平滑,但實(shí)際上,Glide幾乎能滿足你對(duì)遠(yuǎn)程圖片的拉取/縮放/顯示的一切需求。

API

Glide 使用簡(jiǎn)明的流式語(yǔ)法API,這是一個(gè)非常棒的設(shè)計(jì),因?yàn)樗试S你在大部分情況下一行代碼搞定需求:

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

Generated API

Glide v4 使用 注解處理器 (Annotation Processor) 來(lái)生成出一個(gè) API,在 Application 模塊中可使用該流式 API 一次性調(diào)用到 RequestBuilder, RequestOptions 和集成庫(kù)中所有的選項(xiàng)。

Generated API 模式的設(shè)計(jì)出于以下兩個(gè)目的:

  1. 集成庫(kù)可以為 Generated API 擴(kuò)展自定義選項(xiàng)。
  2. 在 Application 模塊中可將常用的選項(xiàng)組打包成一個(gè)選項(xiàng)在 Generated API 中使用

雖然以上所說(shuō)的工作均可以通過(guò)手動(dòng)創(chuàng)建 RequestOptions 子類的方式來(lái)完成,但想將它用好更具有挑戰(zhàn),并且降低了 API 使用的流暢性。

GlideApp.with(fragment)
   .load(myUrl)
   .placeholder(R.drawable.placeholder)
   .fitCenter()
   .into(imageView);

性能

Glide 充分考慮了Android圖片加載性能的兩個(gè)關(guān)鍵方面:

  • 圖片解碼速度
  • 解碼圖片帶來(lái)的資源壓力

為了讓用戶擁有良好的App使用體驗(yàn),圖片不僅要快速加載,而且還不能因?yàn)檫^(guò)多的主線程I/O或頻繁的垃圾回收導(dǎo)致頁(yè)面的閃爍和抖動(dòng)現(xiàn)象。
Glide使用了多個(gè)步驟來(lái)確保在Android上加載圖片盡可能的快速和平滑:

  • 自動(dòng)、智能地下采樣(downsampling)和緩存(caching),以最小化存儲(chǔ)開銷和解碼次數(shù);
  • 積極的資源重用,例如字節(jié)數(shù)組和Bitmap,以最小化昂貴的垃圾回收和堆碎片影響;
  • 深度的生命周期集成,以確保僅優(yōu)先處理活躍的Fragment和Activity的請(qǐng)求,并有利于應(yīng)用在必要時(shí)釋放資源以避免在后臺(tái)時(shí)被殺掉。

結(jié)構(gòu)


image.png

Glide構(gòu)造

  1. Glide.with()

作用:初始化glide,返回一個(gè)RequestManger對(duì)象
以傳入的是FragmentActivity為例,getRetriever (activity)流程如下:


image

initializeGlide主要做了以下工作:

  • 獲取應(yīng)用中帶注解的GlideModule(annotationGeneratedModule),這里要解釋一下GlideModule:用戶自定義glide配置模塊,用來(lái)修改默認(rèn)的glide配置信息。如果這個(gè)為空或者可配置menifest里面的標(biāo)志為true,則獲取menifest里面配置的GlideModule模塊(manifestModules)。
  • 把manifestModules以及annotationGeneratedModule里面的配置信息放到builder里面(applyOptions)替換glide默認(rèn)組件(registerComponents)
    各種初始化信息Glide glide = builder.build(applicationContext);
    看一下build的源碼,主要做了以下工作:
  • 創(chuàng)建請(qǐng)求圖片線程池sourceExecutor,創(chuàng)建硬盤緩存線程池diskCacheExecutor。動(dòng)畫線程池animationExecutor
  • 依據(jù)設(shè)備的屏幕密度和尺寸設(shè)置各種pool的size
  • 創(chuàng)建圖片線程池LruBitmapPool,緩存所有被釋放的bitmap, LruBitmapPool依賴默認(rèn)的緩存策略和緩存配置。緩存策略在API大于19時(shí),為SizeConfigStrategy,小于為AttributeStrategy。其中SizeConfigStrategy是以bitmap的size和config為key,value為bitmap的HashMap
  • 創(chuàng)建對(duì)象數(shù)組緩存池LruArrayPool,默認(rèn)4M
  • 創(chuàng)建LruResourceCache,內(nèi)存緩存
  • new glide里面 new Registry()注冊(cè)管理任務(wù)執(zhí)行對(duì)象的類(Registry),可以簡(jiǎn)單理解為:Registry是一個(gè)工廠,而其中所有注冊(cè)的對(duì)象都是一個(gè)工廠員工,當(dāng)任務(wù)分發(fā)時(shí),根據(jù)當(dāng)前任務(wù)的性質(zhì),分發(fā)給相應(yīng)員工進(jìn)行處理

先看代碼

 public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }
public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }
 public static RequestManager with(@NonNull Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }
 public static RequestManager with(@NonNull android.app.Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }
 public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
  }

可以看到,with方法有很多,但內(nèi)容基本一致,都是通過(guò) RequestManagerRetriever.get(); 獲取一個(gè) RequestManagerRetriever 對(duì)象 retriever ,然后通過(guò) retriever.get(context); 獲取一個(gè) RequestManager 對(duì)象并返回。這些with方法關(guān)鍵的不同在于傳入的參數(shù)不一致,可以是Context、Activity、Fragment等等。那么為什么要分這么多種呢?其實(shí)我們應(yīng)該都知道:Glide在加載圖片的時(shí)候會(huì)綁定 with(context) 方法中傳入的 context 的生命周期,如果傳入的是 Activity ,那么在這個(gè) Activity 銷毀的時(shí)候Glide會(huì)停止圖片的加載。這樣做的好處是顯而易見的:避免了消耗多余的資源,也避免了在Activity銷毀之后加載圖片從而導(dǎo)致的空指針問(wèn)題。

初始化glide,單例模式

 public static Glide get(@NonNull Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }
    return glide;
  }

防止重復(fù)初始化

 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;
  }

加載自定義的配置例如自定義的modeloader,網(wǎng)絡(luò)加載棧等,注冊(cè)ComponentCallbacks,在低內(nèi)存時(shí) memoryCache, bitmapPool ,arrayPool 釋放到內(nèi)存

 private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      manifestModules = new ManifestParser(applicationContext).parse();
    }

    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);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    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;
  }

構(gòu)建Glide,配置數(shù)據(jù)轉(zhuǎn)換器/解碼器/轉(zhuǎn)碼器/編碼器

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) {
    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();
    // Right now we're only using this parser for HEIF images, which are only supported on OMR1+.
    // If we need this for other file types, we should consider removing this restriction.
    // Note that order here matters. We want to check the ExifInterface parser first for orientation
    // and then fall back to DefaultImageHeaderParser for other fields.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
      registry.register(new ExifInterfaceImageHeaderParser());
    }
    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())
        //省略很多行
        .register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);

    ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
    glideContext =
        new GlideContext(
            context,
            arrayPool,
            registry,
            imageViewTargetFactory,
            defaultRequestOptions,
            defaultTransitionOptions,
            engine,
            logLevel);
  }

模型轉(zhuǎn)換器

轉(zhuǎn)換器 功能
ResourceLoader.StreamFactory 將Android資源ID轉(zhuǎn)換為Uri,在加載成為InputStream
ResourceLoader.UriFactory 將資源ID轉(zhuǎn)換為Uri
ResourceLoader.FileDescriptorFactory 將資源ID轉(zhuǎn)化為ParcelFileDescriptor
ResourceLoader.AssetFileDescriptorFactory 將資源ID轉(zhuǎn)化為AssetFileDescriptor
UnitModelLoader.Factory 不做任何轉(zhuǎn)換,返回源數(shù)據(jù)
ByteBufferFileLoader.Factory 將File轉(zhuǎn)換為ByteBuffer
FileLoader.StreamFactory 將File轉(zhuǎn)換為InputStream
FileLoader.FileDescriptorFactory 將File轉(zhuǎn)化為ParcelFileDescriptor
DataUrlLoader.StreamFactory 將Url轉(zhuǎn)化為InputStream
StringLoader.StreamFactory 將String轉(zhuǎn)換為InputStream
StringLoader.AssetFileDescriptorFactory 將String轉(zhuǎn)換為AssetFileDescriptor
HttpUriLoader.Factory 將http/https Uri轉(zhuǎn)換為InputStream
UriLoader.StreamFactory 將Uri轉(zhuǎn)換為InputStream
UriLoader.FileDescriptorFactory 將Uri轉(zhuǎn)換為ParcelFileDescriptor
UriLoader.AssetFileDescriptorFactory 將Uri轉(zhuǎn)換為AssetFileDescriptor
UrlUriLoader.StreamFactory 將將http/https的Uri轉(zhuǎn)換為InputStream
UrlLoader.StreamFactory 將Url轉(zhuǎn)換為InputStream
HttpGlideUrlLoader.Factory 將HttpGlide轉(zhuǎn)換為InputStream

解碼器

解碼器 功能
ByteBufferGifDecoder 將ByteBuffer解碼為GifDrawable
ByteBufferBitmapDecoder 將ByteBuffer解碼為Bitmap
ResourceDrawableDecoder 將資源Uri解碼為Drawable
ResourceBitmapDecoder 將資源ID解碼為Bitmap
BitmapDrawableDecoder 將數(shù)據(jù)解碼為BitmapDrawable
StreamBitmapDecoder 將InputStreams解碼為Bitmap
StreamGifDecoder 將InputStream數(shù)據(jù)轉(zhuǎn)換為BtyeBuffer,再解碼為GifDrawable
GifFrameResourceDecoder 解碼gif幀
FileDecoder 包裝File成為FileResource
UnitDrawableDecoder 將Drawable包裝為DrawableResource
UnitBitmapDecoder 包裝Bitmap成為BitmapResource
VideoDecoder 將本地視頻文件解碼為Bitmap

編碼器

編碼器 功能
ByteBufferEncoder 將Byte數(shù)據(jù)緩存為File
StreamEncoder InputStream緩存為File
BitmapEncoder 將Bitmap數(shù)據(jù)緩存為File
BitmapDrawableEncoder 將BitmapDrawable數(shù)據(jù)緩存為File
GifDrawableEncoder 將GifDrawable數(shù)據(jù)緩存為File

Glide的加載流程可以概括為以下流程:

model(數(shù)據(jù)源)-->data(轉(zhuǎn)換數(shù)據(jù))-->decode(解碼)-->transformed(縮放)-->transcoded(轉(zhuǎn)碼)-->encoded(編碼保存到本地)

Glide 生命周期管理

  1. ComponentCallbacks
public interface ComponentCallbacks2{
    //當(dāng)?shù)蛢?nèi)存時(shí)回調(diào),
    void onTrimMemory(int );
}

當(dāng)回調(diào)onTrimMemory(int level)執(zhí)行以下代碼,
釋放 memoryCache bitmapPool arrayPool 一些對(duì)象

 public void trimMemory(int level) {
    // Engine asserts this anyway when removing resources, fail faster and consistently
    Util.assertMainThread();
    // memory cache needs to be trimmed before bitmap pool to trim re-pooled Bitmaps too. See #687.
    memoryCache.trimMemory(level);
    bitmapPool.trimMemory(level);
    arrayPool.trimMemory(level);
  }
  1. 與activity生命周期綁定,在Activity 添加一個(gè)不可見的RequestManagerFragment,監(jiān)聽其生命周期來(lái)決定當(dāng)前加載任務(wù)需不需要取消
private RequestManagerFragment getRequestManagerFragment(
      @NonNull final android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        current = new RequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        pendingRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }
  1. 如何加載GIF 文件
    注意看GifFrameLoader,注意邏輯在這里,用handle不斷l(xiāng)oadNextFrame;
  SourceGenerator-->HttpUrlFetcher-->inputstream (網(wǎng)絡(luò)獲取數(shù)據(jù))-> lrudiskcache 保存到本地-->DataCacheGenerator-->ByteBufferFetcher-->ByteBuffer-->byteBufferGifDecoder-->GifDrawableResource-->GifDrawable --> GifFrameLoader  (這里面就開始循壞顯示圖片)
1609120f27c3cf3a.png
111111.png
整體時(shí)序圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容