Glide V4使用指南

595349-6ffdbb24d4945fa4.png

Glide的強大和靈活相信不需要多介紹了

本文使用Glide版本爲4.8.0,因爲使用的Java語言進行開發,涉及到使用Kotlin的部分還請參考官方文檔

SDK要求

  • 最小SDK版本需要使用API 14(或者更高版本)
  • Complie SDK Version需要使用API 27(或者更高版本)
  • Glide使用的SupportLibrary 版本是27,如果需要不同的SupportLibrary版本可以用excludeGlideSupportLibrary從依賴中去掉,具體的在集成時說明

一、集成

在項目的build.gradle文件中添加google()倉庫

repositories {
    google()
    //or maven { url 'https://maven.google.com' }
    jcenter()
}

在要使用的Glidemodule中添加以下代碼

implementation('com.github.bumptech.glide:glide:4.8.0')
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
//如果使用了在Kotlin中使用了Glide註解,需要引入kapt依賴代替annotationProcessor依賴
//kapt 'com.github.bumptech.glide:compiler:4.8.0'

若使用了不是27的SupportLibrary版本,使用以下代碼

implementation('com.github.bumptech.glide:glide:4.8.0') {
    exclude group: "com.android.support"
}
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'

使用了不同版本的SupportLibrary可能導致一些運行時異常,

java.lang.NoSuchMethodError: No static method getFont(Landroid/content/Context;ILandroid/util/TypedValue;ILandroid/widget/TextView;)Landroid/graphics/Typeface; in class Landroid/support/v4/content/res/ResourcesCompat; or its super classes (declaration of 'android.support.v4.content.res.ResourcesCompat' 
at android.support.v7.widget.TintTypedArray.getFont(TintTypedArray.java:119)

儘量避免使用@aar,如果需要這麼做,需要添加transitive=true確保有所需要的類。

dependencies {
    implementation ("com.github.bumptech.glide:glide:4.8.0@aar") {
        transitive = true
    }
}

@aarGradle的限制符,默認會去除使用到得依賴。如果不添加transitive=true的話將會導致運行時異常。

java.lang.NoClassDefFoundError: com.bumptech.glide.load.resource.gif.GifBitmapProvider
    at com.bumptech.glide.load.resource.gif.ByteBufferGifDecoder.<init>(ByteBufferGifDecoder.java:68)
    at com.bumptech.glide.load.resource.gif.ByteBufferGifDecoder.<init>(ByteBufferGifDecoder.java:54)
    at com.bumptech.glide.Glide.<init>(Glide.java:327)
    at com.bumptech.glide.GlideBuilder.build(GlideBuilder.java:445)
    at com.bumptech.glide.Glide.initializeGlide(Glide.java:257)
    at com.bumptech.glide.Glide.initializeGlide(Glide.java:212)
    at com.bumptech.glide.Glide.checkAndInitializeGlide(Glide.java:176)
    at com.bumptech.glide.Glide.get(Glide.java:160)
    at com.bumptech.glide.Glide.getRetriever(Glide.java:612)
    at com.bumptech.glide.Glide.with(Glide.java:684)

權限

這個不需要多說,如果加載網絡圖片必然需要網絡權限,這個還是根據具體的使用情況而定。

<uses-permission android:name="android.permission.INTERNET" />

Glide添加了鏈接監聽(Connectivity Monitoring),通過網絡加載圖片時,Glide可以監聽鏈接狀態並在重新鏈接到網絡時重啓之前失敗的請求。使用鏈接監聽我們需要添加ACCESS_NETWORK_STATE權限,Glide將自動監聽連接狀態,不需要額外的改動。

如果需要使用ExternalPreferredCacheDiskCacheFactory將Glide的緩存存放到SD卡上,還需要添加READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE權限。

混淆

如果使用了混淆,需要添加以下的代碼到混淆的配置文件中

-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}

如果使用的target API低於27的話還需要添加

-dontwarn com.bumptech.glide.load.resource.bitmap.VideoDecoder

VideoDecoder使用API 27的一些藉口,可能導致混淆發出警告。

如果使用了DexGuard,可能還需要添加

-keepresourcexmlelements manifest/application/meta-data@value=GlideModule

二、簡單使用

V4的基本用法還是沒變,一句簡單地鏈式調用就能講圖片加載到ImageView上了

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

這裏context就是一個Context對象,而url是一個網絡圖片鏈接,imageView則是需要顯示圖片的ImageView。V4還可以手動取消加載,例如:

Glide.with(context).clear(imageView);

Glide.with()中傳入的ActivityFragment銷燬時,Glide會自動取消加載並回收資源
這裏會提出clear方法,在RecyclerView中複用View來加載圖片,每次需要調用Glide重新進行加載操作或者使用clear方法停止之前的請求,避免圖片顯示錯亂的問題。

可以看到with方法還是很強大,支持ContextActivityFragmentView等,只是將原本支持傳入android.app.Fragment的方法標記爲過期了。

load方法也提供了強大的支持,Uri、文件、btye[]、網絡連接、資源id、bitmap、drawable都可以加載。

Generated API(生成API)

Glide V4使用註解處理器生成一個流式API,用於RequestBuilderRequestOptions等相關的所有選項。Glide的文檔中有說明,其目的一是爲了更好地擴展自定義選項,其二是爲了方便打包常用選項組。

RequestOptions的設置,Glide V4還提供了apply方法設置單次請求的選項,以及applyDefaultRequestOptions設置默認請求選項。

從Glide V3到Glide V4的升級,生成API是一個很大的改變,接下來詳細說明。

生成API目前只能在Application模塊中使用,無法同時在各個LibraryApplication中定義各自的生成API,確保了調用API時請求的選項是一致的。官方也指出,在將來的版本中可能解除此限制。

使用Generated API僅僅需要兩步,第一步,在build.gradle的依賴中添加annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'上面其實已經給出;第二步,我們需要創建一個class繼承AppGlideModule,併爲該類添加@GlideModule註解。

package com.mrtrying.glidev4_example;

import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;

GlideModule
public final class ExampleAppGlideModule extends AppGlideModule {}

AppGlideModule雖然是抽象類,卻可以不用重寫任何方法。但必須使用@GlideModule註解標記該類,否則沒法順利的生成GlideAppAPI

PS:第一次添加AppGlideModule或者對AppGlideModule做了某些修改時,我們需要重新構建項目重新生成API。如果AndroidStudio沒法自動完成構建,可以使用Build—->Rebuild Project手動重新構建,必要時可能需要手動刪除modulebuild目錄再重新構建項目。

生成API的默認名爲GlideApp,其包名與所在的module的包名相同,而基本用法也於之前相同,只是將Glide替換成了GlideApp,就可以使用了

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

Glide生成API還有GlideExtensionGlideOptionGlideType,暫時只對AppGlideModule進行說明,其他的涉及到較深度的使用,放在後面說明。

佔位符

Glide提供了三種不同類型的佔位符:

  • placeholer(佔位符)
  • error(錯誤符)
  • fallback(後備回調符)
GlideApp.with(this)
        .load(url)
        .placeholder(R.mipmap.placeholder)
        .error(R.mipmap.error)
        .fallback(R.mipmap.fallback)
        .into(imageView);

先說API的參數,placeholdererrorfallback相同可以傳入資源id或者Drawable對象。

接下來說說這三個佔位符的調用時機,用過V3的同學一定知道placeholer佔位符是當資源正在請求是被展示的圖片或者資源文件,當請求成功時佔位符會被替換成請求到得資源;error則是在請求永久性失敗是展示;fallback是當請求的url(或者model)爲null時展示,此佔位符的目的是設置null是否是可接收的正常情況,而Glide將null作爲錯誤處理。

三個佔位符對應了三種時機,其中還有一些狀態,無可避免的會出現一些複雜的情況需要說明:

  • 設置了placeholder之後,如果請求失敗,但是沒有設置error,那麼佔位符將繼續顯示;而請求的url/model爲null也會導致請求失敗,一樣的,沒有設置errorfallback的話,佔位符也將繼續顯示
  • 如果請求的url/model爲null導致請求失敗,fallback會優先於error調用,也就是說url爲null時設置了error而沒有設置fallback將繼續顯示error;反之,設置了fallback就會顯示fallback

佔位符這個功能確實很棒,有朋友不經要問了,如果我想加載網絡圖片怎麼辦?這個就十分抱歉了,Glide並不提供這樣的功能,只能支持資源文件或者是Drawable對象。當然,你也可以提前下載要網絡圖片在以Drawable形式加載,只要這樣的效率你可以接收(個人覺得沒有什麼必要)。

官方文檔在佔位符最後的FAQ中有說明,佔位符是在主線程中,從Android Resource加載的,而且Glide的Transformation不會被應用到佔位符上。這都是希望在使用佔位符時能儘量少得佔用系統資源來考慮的,所以Glide並沒有提供他們認爲不太合理的API。

關於圖片的變換,比較常用到得是圓角和模糊,圓角的處理的話比較推薦RCLayout,雖然在背景的處理上會有一點鋸齒,不過還是很不錯的一個庫;而模糊的話推薦使用android自帶的RenderScript,相關方法在API 17以上提供,也提供了相關的support包。

transformation(變換)

Glide提供了transformation得功能,在獲取到請求的圖片之後,能對圖片進行一些處理,例如:裁剪、模糊等;而transformation的強大在於可以自定義,這樣一來transformation不僅能處理bitmap,同樣可以用於處理GIF動畫,還有自定義資源類型。

Glide在API上提供的了5個相關的方法可以直接使用

  • circleCrop()
  • centerCrop()
  • centerInside()
  • fitCenter()
  • optionalFitCenter()

optionalFitCenter()的具體作用沒有太清楚,效果與fitCenter()相同,有知道的大佬請告知

使用方式基本和佔位符一致,在load()方法後調用相關方法就能使用相應的transformation效果了

GlideApp.with(this)
        .load(url)
        .centerCrop()
        .into(imageView);

這是在生成API中的使用,在Glide普通的API也是可以使用的,不過沒有直接可以調用的方法,需要通過RequestOptions來設置

RequestOptions options = new RequestOptions().centerCrop();

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

除了可以自己創建以外,Glide提供了靜態獲取RequestOptions方法,可以直接使用

Glide.with(this)
        .load(url)
        .apply(RequestOptions.centerCropTransform())
        .into(imageView);

針對ImageView可能自身也會設置scaleType的情況,Glide在部分情況會自動應用 FitCenterCenterCrop,如果 scaleTypeCENTER_CROP , Glide 將會自動應用 CenterCrop 變換。如果 scaleTypeFIT_CENTERCENTER_INSIDEGlide會自動使用 FitCenter 變換。
當然,設置了其他的變換也可以將其覆蓋;或者使用dontTransform()方法就不會在執行任何變換。

這裏需要注意的是,Glide內置的這幾種transformation,即使在使用多種變換,也只有最後一個transformation會生效。這個不僅僅是對Glide內置的transformation也包括transform()設置的transformation,都會替換掉之前的transformation

如果需要支持多種變換,需要使用transform()設置MultiTransformation類對象傳入,MultiTransformation的構造器可以接收可變參數或者Transformation的集合;或者在transforms()方法中設置多個(不要以爲是一個方法,這是transforms(),結尾有s的)而這些個變化的應用順序就是傳入參數的順序。

GlideApp.with(this)
        .load(url)
        .transform(new MultiTransformation<>(new FitCenter(),new RoundedCorners(3)))
        .into(imageView);
//或者
GlideApp.with(this)
        .load(url)
        .transforms(new FitCenter(),new RoundedCorners(3))
        .into(imageView);

由於Transformation是沒有狀態的,我們可以再多個加載中複用同一個Transformation對象。

自定義transformation

爲了方便使用,我們希望一些特殊的變換也能像上面一樣的使用,這種情況可以通過實現Transformation來處理。

這裏直接以Glide內置實現的RoundedCorners爲例

public final class RoundedCorners extends BitmapTransformation {
  private static final String ID = "com.bumptech.glide.load.resource.bitmap.RoundedCorners";
  private static final byte[] ID_BYTES = ID.getBytes(CHARSET);

  private final int roundingRadius;

  /**
   * @param roundingRadius the corner radius (in device-specific pixels).
   * @throws IllegalArgumentException if rounding radius is 0 or less.
   */
  public RoundedCorners(int roundingRadius) {
    Preconditions.checkArgument(roundingRadius > 0, "roundingRadius must be greater than 0.");
    this.roundingRadius = roundingRadius;
  }

  @Override
  protected Bitmap transform(
      @NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
    return TransformationUtils.roundedCorners(pool, toTransform, roundingRadius);
  }

  @Override
  public boolean equals(Object o) {
    if (o instanceof RoundedCorners) {
      RoundedCorners other = (RoundedCorners) o;
      return roundingRadius == other.roundingRadius;
    }
    return false;
  }

  @Override
  public int hashCode() {
    return Util.hashCode(ID.hashCode(),
        Util.hashCode(roundingRadius));
  }

  @Override
  public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
    messageDigest.update(ID_BYTES);

    byte[] radiusData = ByteBuffer.allocate(4).putInt(roundingRadius).array();
    messageDigest.update(radiusData);
  }
}

可以看到RoundedCorners是繼承自BitmapTransformationBitmapTransformation已經幫我們處理了部分基礎邏輯,比如提取和回收原始的Bitmap,而我們處理好Bitmap的變換就可以了。

除了自身的構造函數意外還重寫了4個方法,其中transform方法就是進行變換處理的方法,變換完成之後返回變換完成的Bitmap。其餘的三個方法,updateDiskCacheKey是必須實現的,而爲例保證內存和磁盤緩存正常,equals()hashCode()也是必須重寫的。

RoundedCorners使用完整報名路徑的限定名來作爲一個 ID,它可以構成 hashCode() 的基礎,並可用於更新 updateDiskCacheKey() 傳入的 MessageDigest。如果你的 Transformation 需要參數而且它會影響到 Bitmap 被變換的方式,它們也必須被包含到這三個方法中,就像RoundedCorners一樣。原來的ID保留,但roundingRadius也包含到了這個三個方法中,updateDiskCacheKey 方法還演示了你可以如何使用 ByteBuffer 來包含基本參數到你的 updateDiskCacheKey 實現中。

官方文檔着重指出必須要重寫equals()hashCode()方法,雖然不重寫不會出現編譯問題,但是這不代表能正常工作。

三、進階

Target

或許在使用into()時有些印象,into()方法可以直接傳入ImageView也可以使用Target。其實跟蹤一下Glide的源碼就會發現其實在傳入ImageView時,最終也在ImageViewTargetFactory的包裝下返回了ImageViewTarget,而ViewTarget的最上層的父類就是Target

into()方法不僅用於啓動每一個請求,同時也制定了接收請求結果的Target

Target<Drawable> target = GlideApp.with(this)
        .load(url)
        .centerCrop()
        .into(new Target<Drawable>() {
            //Target的方法太多,這裏代碼省略...
        });

如果重用同一個Target對加載一個新的請求,那麼之前的的請求都會被取消並且釋放資源。我們也可以使用clear()方法對不需要重新加載的請求進行相關資源的釋放。

GlideApp.with(this).clear(target);

上面的情況中直接使用ImageVIew也是可以的。因爲ViewTarget使用了setTag()getTag()存儲了Request,所以可以直接從Viewtag取回之前一次加載的信息,也是是ImageVIew的默認tag被佔用的原因。

也就是說,在使用ImageVIew的情況中,即使使用同一個ImageView重新加載也是可以釋放之前的請求和資源的

Glide.with(this)
        .load(url)
        .into(imageView);
//加載新連接
Glide.with(this)
        .load(newUrl)
        .into(imageView);

只要繼承ViewTarget或者重寫setRequest()getRequest()並實現取回上一次加載的信息,重用的機制就可以得以保證。

Transitions(過渡)

Glide V3和V4不同,不會默認應用交叉淡入或任何其他的過渡效果,每個請求需要手動應用過渡。Glide提供了很多過渡動畫,我們可以手動應用於每一個請求上;內置過渡的運行方式是一致的,會根據加載圖像不同的情況來決定是否執行過渡。例如:如果Glide從內存緩存中加載出來,Glide的內置過渡將不會執行;而加載磁盤緩存、本地文件或者遠程連接時都會執行Glide的內置過渡。

可以通過transition()方法設置TransitionOptions

Glide.with(this)
    .load(url)
    .transition(DrawableTransitionOptions.withCrossFade())
    .into(imageView);

TransitionOptions用於給一個特定的請求指定過渡,而不同的資源類型能決定使用什麼類型的過渡選項。Bitmap 和 Drawable可以對應使用使用 BitmapTransitionOptionsDrawableTransitionOptions 來指定類型特定的過渡動畫。對於 BitmapDrawable 之外的資源類型,可以使用 GenericTransitionOptions

如果需要自定義過渡動畫,我們需要通過DrawableTransitionOptions.with()生成我們自己的TransitionOptions,而with()需要出入一個TransitionFactory對象。TransitionFactory是一個接口我們需要實現一個這樣的類

public class ExampleTransitionFactory implements TransitionFactory {
    @Override
    public Transition build(DataSource dataSource, boolean isFirstResource) {
        return null;
    }
}

通過DrawableTransitionOptions.with(new ExampleTransitionFactory())我們就能調用transition()方法來加載我們的定製的過渡動畫。transition()方法支持動畫的資源id,AnimatorTransitionFactory,可以通過這三種方式來實現動畫部分。

595349-ca6194b5bae23408.png

最後就是執行transition()來加載我們自定義的動畫

GlideApp.with(this)
        .load(url)
        .transition(DrawableTransitionOptions.with(new ExampleTransitionFactory()).transition(R.anim.show))
        .into(imageView);

這裏需要提醒的是,動畫對於性能的開銷不用多說,比圖片解碼本身還要耗時,在列表的快速滑動的情況下可能造成加載緩慢。在列表中考慮是否使用動畫,在一些希望圖片儘快加載出來的時候也需要做此考慮。

未完待續...

如果喜歡該文章,可以掃碼領個紅包支持一下

595349-a17fa0f3e660f483.png
圖片發自簡書App
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章