天天看點

Android圖檔加載架構最全解析(八),帶你全面了解Glide 4的用法

本篇将是我們這個Glide系列的最後一篇文章。

其實在寫這個系列第一篇文章的時候,Glide就推出4.0.0的RC版了。那個時候因為我一直研究的都是Glide 3.7.0版本,再加上RC版本還不太穩定,是以整個系列也都是基于3.7.0版本來寫的。

而現在,Glide的最新版本已經出到了4.4.0,可以說Glide 4已經是相當成熟和穩定了。而且也不斷有朋友一直在留言,想讓我講一講Glide 4的用法,因為Glide 4相對于Glide 3改動貌似還是挺大的,學完了Glide 3再去使用Glide 4,發現根本就無法使用。

OK,那麼今天就讓我們用《帶你全面了解Glide 4的用法》這樣一篇文章,給這個Glide系列畫上一個圓滿的句号。

Glide 4概述

剛才有說到,有些朋友覺得Glide 4相對于Glide 3改動非常大,其實不然。之是以大家會有這種錯覺,是因為你将Glide 3的用法直接搬到Glide 4中去使用,結果IDE全面報錯,然後大家可能就覺得Glide 4的用法完全變掉了。

其實Glide 4相對于Glide 3的變動并不大,隻是你還沒有了解它的變動規則而已。一旦你掌握了Glide 4的變動規則之後,你會發現大多數Glide 3的用法放到Glide 4上都還是通用的。

我對Glide 4進行了一個大概的研究之後,發現Glide 4并不能算是有什麼突破性的更新,而更多是一些API工整方面的優化。相比于Glide 3的API,Glide 4進行了更加科學合理地調整,使得易讀性、易寫性、可擴充性等方面都有了不錯的提升。但如果你已經對Glide 3非常熟悉的話,并不是就必須要切換到Glide 4上來,因為Glide 4上能實作的功能Glide 3也都能實作,而且Glide 4在性能方面也并沒有什麼提升。

但是對于新接觸Glide的朋友而言,那就沒必要再去學習Glide 3了,直接上手Glide 4就是最佳的選擇了。

好了,對Glide 4進行一個基本的概述之後,接下來我們就要正式開始學習它的用法了。剛才我已經說了,Glide 4的用法相對于Glide 3其實改動并不大。在前面的七篇文章中,我們已經學習了Glide 3的基本用法、緩存機制、回調與監聽、圖檔變換、自定義子產品等用法,那麼今天這篇文章的目标就很簡單了,就是要掌握如何在Glide 4上實作之前所學習過的所有功能,那麼我們現在就開始吧。

開始

要想使用Glide,首先需要将這個庫引入到我們的項目當中。建立一個Glide4Test項目,然後在app/build.gradle檔案當中添加如下依賴:

dependencies {
    implementation \'com.github.bumptech.glide:glide:4.4.0\'
    annotationProcessor \'com.github.bumptech.glide:compiler:4.4.0\'
}           

注意,相比于Glide 3,這裡要多添加一個compiler的庫,這個庫是用于生成Generated API的,待會我們會講到它。

另外,Glide中需要用到網絡功能,是以你還得在AndroidManifest.xml中聲明一下網絡權限才行:

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

就是這麼簡單,然後我們就可以自由地使用Glide中的任意功能了。

加載圖檔

現在我們就來嘗試一下如何使用Glide來加載圖檔吧。比如這是一張圖檔的位址:

http://guolin.tech/book.png           

然後我們想要在程式當中去加載這張圖檔。

那麼首先打開項目的布局檔案,在布局當中加入一個Button和一個ImageView,如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Load Image"
        android:onClick="loadImage"
        />

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>           

為了讓使用者點選Button的時候能夠将剛才的圖檔顯示在ImageView上,我們需要修改MainActivity中的代碼,如下所示:

public class MainActivity extends AppCompatActivity {

    ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.image_view);
    }

    public void loadImage(View view) {
        String url = "http://guolin.tech/book.png";
        Glide.with(this).load(url).into(imageView);
    }

}           

沒錯,就是這麼簡單。現在我們來運作一下程式,效果如下圖所示:

Android圖檔加載架構最全解析(八),帶你全面了解Glide 4的用法

可以看到,一張網絡上的圖檔已經被成功下載下傳,并且展示到ImageView上了。

你會發現,到目前為止,Glide 4的用法和Glide 3是完全一樣的,實際上核心的代碼就隻有這一行而已:

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

仍然還是傳統的三步走:先with(),再load(),最後into()。對這行代碼的解讀,我在 Android圖檔加載架構最全解析(一),Glide的基本用法 這篇文章中講解的很清楚了,這裡就不再贅述。

好了,現在你已經成功入門Glide 4了,那麼接下來就讓我們學習一下Glide 4的更多用法吧。

占位圖

觀察剛才加載網絡圖檔的效果,你會發現,點選了Load Image按鈕之後,要稍微等一會圖檔才會顯示出來。這其實很容易了解,因為從網絡上下載下傳圖檔本來就是需要時間的。那麼我們有沒有辦法再優化一下使用者體驗呢?當然可以,Glide提供了各種各樣非常豐富的API支援,其中就包括了占位圖功能。

顧名思義,占位圖就是指在圖檔的加載過程中,我們先顯示一張臨時的圖檔,等圖檔加載出來了再替換成要加載的圖檔。

下面我們就來學習一下Glide占位圖功能的使用方法,首先我事先準備好了一張loading.jpg圖檔,用來作為占位圖顯示。然後修改Glide加載部分的代碼,如下所示:

RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.loading);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);           

沒錯,就是這麼簡單。這裡我們先建立了一個RequestOptions對象,然後調用它的placeholder()方法來指定占位圖,再将占位圖檔的資源id傳入到這個方法中。最後,在Glide的三步走之間加入一個apply()方法,來應用我們剛才建立的RequestOptions對象。

不過如果你現在重新運作一下代碼并點選Load Image,很可能是根本看不到占位圖效果的。因為Glide有非常強大的緩存機制,我們剛才加載圖檔的時候Glide自動就已經将它緩存下來了,下次加載的時候将會直接從緩存中讀取,不會再去網絡下載下傳了,因而加載的速度非常快,是以占位圖可能根本來不及顯示。

是以這裡我們還需要稍微做一點修改,來讓占位圖能有機會顯示出來,修改代碼如下所示:

RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.loading)
        .diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);           

可以看到,這裡在RequestOptions對象中又串接了一個diskCacheStrategy()方法,并傳入DiskCacheStrategy.NONE參數,這樣就可以禁用掉Glide的緩存功能。

關于Glide緩存方面的内容我們待會兒會進行更詳細的講解,這裡隻是為了測試占位圖功能而加的一個額外配置,暫時你隻需要知道禁用緩存必須這麼寫就可以了。

現在重新運作一下代碼,效果如下圖所示:

Android圖檔加載架構最全解析(八),帶你全面了解Glide 4的用法

可以看到,當點選Load Image按鈕之後會立即顯示一張占位圖,然後等真正的圖檔加載完成之後會将占位圖替換掉。

除了這種加載占位圖之外,還有一種異常占位圖。異常占位圖就是指,如果因為某些異常情況導緻圖檔加載失敗,比如說手機網絡信号不好,這個時候就顯示這張異常占位圖。

異常占位圖的用法相信你已經可以猜到了,首先準備一張error.jpg圖檔,然後修改Glide加載部分的代碼,如下所示:

RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.ic_launcher_background)
        .error(R.drawable.error)
        .diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);           

很簡單,這裡又串接了一個error()方法就可以指定異常占位圖了。

其實看到這裡,如果你熟悉Glide 3的話,相信你已經掌握Glide 4的變化規律了。在Glide 3當中,像placeholder()、error()、diskCacheStrategy()等等一系列的API,都是直接串聯在Glide三步走方法中使用的。

而Glide 4中引入了一個RequestOptions對象,将這一系列的API都移動到了RequestOptions當中。這樣做的好處是可以使我們擺脫冗長的Glide加載語句,而且還能進行自己的API封裝,因為RequestOptions是可以作為參數傳入到方法中的。

比如你就可以寫出這樣的Glide加載工具類:

public class GlideUtil {

    public static void load(Context context,
                            String url,
                            ImageView imageView,
                            RequestOptions options) {
        Glide.with(context)
             .load(url)
             .apply(options)
             .into(imageView);
    }

}           

指定圖檔大小

實際上,使用Glide在大多數情況下我們都是不需要指定圖檔大小的,因為Glide會自動根據ImageView的大小來決定圖檔的大小,以此保證圖檔不會占用過多的記憶體進而引發OOM。

不過,如果你真的有這樣的需求,必須給圖檔指定一個固定的大小,Glide仍然是支援這個功能的。修改Glide加載部分的代碼,如下所示:

RequestOptions options = new RequestOptions()
        .override(200, 100);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);           

仍然非常簡單,這裡使用override()方法指定了一個圖檔的尺寸。也就是說,Glide現在隻會将圖檔加載成200*100像素的尺寸,而不會管你的ImageView的大小是多少了。

如果你想加載一張圖檔的原始尺寸的話,可以使用Target.SIZE_ORIGINAL關鍵字,如下所示:

RequestOptions options = new RequestOptions()
        .override(Target.SIZE_ORIGINAL);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);           

這樣的話,Glide就不會再去自動壓縮圖檔,而是會去加載圖檔的原始尺寸。當然,這種寫法也會面臨着更高的OOM風險。

緩存機制

Glide的緩存設計可以說是非常先進的,考慮的場景也很周全。在緩存這一功能上,Glide又将它分成了兩個子產品,一個是記憶體緩存,一個是硬碟緩存。

這兩個緩存子產品的作用各不相同,記憶體緩存的主要作用是防止應用重複将圖檔資料讀取到記憶體當中,而硬碟緩存的主要作用是防止應用重複從網絡或其他地方重複下載下傳和讀取資料。

記憶體緩存和硬碟緩存的互相結合才構成了Glide極佳的圖檔緩存效果,那麼接下來我們就來分别學習一下這兩種緩存的使用方法。

首先來看記憶體緩存。

你要知道,預設情況下,Glide自動就是開啟記憶體緩存的。也就是說,當我們使用Glide加載了一張圖檔之後,這張圖檔就會被緩存到記憶體當中,隻要在它還沒從記憶體中被清除之前,下次使用Glide再加載這張圖檔都會直接從記憶體當中讀取,而不用重新從網絡或硬碟上讀取了,這樣無疑就可以大幅度提升圖檔的加載效率。比方說你在一個RecyclerView當中反複上下滑動,RecyclerView中隻要是Glide加載過的圖檔都可以直接從記憶體當中迅速讀取并展示出來,進而大大提升了使用者體驗。

而Glide最為人性化的是,你甚至不需要編寫任何額外的代碼就能自動享受到這個極為便利的記憶體緩存功能,因為Glide預設就已經将它開啟了。

那麼既然已經預設開啟了這個功能,還有什麼可講的用法呢?隻有一點,如果你有什麼特殊的原因需要禁用記憶體緩存功能,Glide對此提供了接口:

RequestOptions options = new RequestOptions()
        .skipMemoryCache(true);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);           

可以看到,隻需要調用skipMemoryCache()方法并傳入true,就表示禁用掉Glide的記憶體緩存功能。

接下來我們開始學習硬碟緩存方面的内容。

其實在剛剛學習占位圖功能的時候,我們就使用過硬碟緩存的功能了。當時為了禁止Glide對圖檔進行硬碟緩存而使用了如下代碼:

RequestOptions options = new RequestOptions()
        .diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);           

調用diskCacheStrategy()方法并傳入DiskCacheStrategy.NONE,就可以禁用掉Glide的硬碟緩存功能了。

這個diskCacheStrategy()方法基本上就是Glide硬碟緩存功能的一切,它可以接收五種參數:

  • DiskCacheStrategy.NONE: 表示不緩存任何内容。
  • DiskCacheStrategy.DATA: 表示隻緩存原始圖檔。
  • DiskCacheStrategy.RESOURCE: 表示隻緩存轉換過後的圖檔。
  • DiskCacheStrategy.ALL : 表示既緩存原始圖檔,也緩存轉換過後的圖檔。
  • DiskCacheStrategy.AUTOMATIC: 表示讓Glide根據圖檔資源智能地選擇使用哪一種緩存政策(預設選項)。

其中,DiskCacheStrategy.DATA對應Glide 3中的DiskCacheStrategy.SOURCE,DiskCacheStrategy.RESOURCE對應Glide 3中的DiskCacheStrategy.RESULT。而DiskCacheStrategy.AUTOMATIC是Glide 4中新增的一種緩存政策,并且在不指定diskCacheStrategy的情況下預設使用就是的這種緩存政策。

上面五種參數的解釋本身并沒有什麼難了解的地方,但是關于轉換過後的圖檔這個概念大家可能需要了解一下。就是當我們使用Glide去加載一張圖檔的時候,Glide預設并不會将原始圖檔展示出來,而是會對圖檔進行壓縮和轉換(我們會在稍後學習這方面的内容)。總之就是經過種種一系列操作之後得到的圖檔,就叫轉換過後的圖檔。

好的,關于Glide 4硬碟緩存的内容就講到這裡。想要了解更多Glide緩存方面的知識,可以參考 Android圖檔加載架構最全解析(三),深入探究Glide的緩存機制 這篇文章。

指定加載格式

我們都知道,Glide其中一個非常亮眼的功能就是可以加載GIF圖檔,而同樣作為非常出色的圖檔加載架構的Picasso是不支援這個功能的。

而且使用Glide加載GIF圖并不需要編寫什麼額外的代碼,Glide内部會自動判斷圖檔格式。比如我們将加載圖檔的URL位址改成一張GIF圖,如下所示:

Glide.with(this)
     .load("http://guolin.tech/test.gif")
     .into(imageView);           

現在重新運作一下代碼,效果如下圖所示:

Android圖檔加載架構最全解析(八),帶你全面了解Glide 4的用法

也就是說,不管我們傳入的是一張普通圖檔,還是一張GIF圖檔,Glide都會自動進行判斷,并且可以正确地把它解析并展示出來。

但是如果我想指定加載格式該怎麼辦呢?就比如說,我希望加載的這張圖必須是一張靜态圖檔,我不需要Glide自動幫我判斷它到底是靜圖還是GIF圖。

想實作這個功能仍然非常簡單,我們隻需要再串接一個新的方法就可以了,如下所示:

Glide.with(this)
     .asBitmap()
     .load("http://guolin.tech/test.gif")
     .into(imageView);           

可以看到,這裡在with()方法的後面加入了一個asBitmap()方法,這個方法的意思就是說這裡隻允許加載靜态圖檔,不需要Glide去幫我們自動進行圖檔格式的判斷了。如果你傳入的還是一張GIF圖的話,Glide會展示這張GIF圖的第一幀,而不會去播放它。

熟悉Glide 3的朋友對asBitmap()方法肯定不會陌生對吧?但是千萬不要覺得這裡就沒有陷阱了,在Glide 3中的文法是先load()再asBitmap()的,而在Glide 4中是先asBitmap()再load()的。乍一看可能分辨不出來有什麼差別,但如果你寫錯了順序就肯定會報錯了。

那麼類似地,既然我們能強制指定加載靜态圖檔,就也能強制指定加載動态圖檔,對應的方法是asGif()。而Glide 4中又新增了asFile()方法和asDrawable()方法,分别用于強制指定檔案格式的加載和Drawable格式的加載,用法都比較簡單,就不再進行示範了。

回調與監聽

回調與監聽這部分的内容稍微有點多,我們分成四部分來學習一下。

1. into()方法

我們都知道Glide的into()方法中是可以傳入ImageView的。那麼into()方法還可以傳入别的參數嗎?我們可以讓Glide加載出來的圖檔不顯示到ImageView上嗎?答案是肯定的,這就需要用到自定義Target功能。

Glide中的Target功能多樣且複雜,下面我就先簡單示範一種SimpleTarget的用法吧,代碼如下所示:

SimpleTarget<Drawable> simpleTarget = new SimpleTarget<Drawable>() {
    @Override
    public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
        imageView.setImageDrawable(resource);
    }
};

public void loadImage(View view) {
    Glide.with(this)
         .load("http://guolin.tech/book.png")
         .into(simpleTarget);
}           

這裡我們建立了一個SimpleTarget的執行個體,并且指定它的泛型是Drawable,然後重寫了onResourceReady()方法。在onResourceReady()方法中,我們就可以擷取到Glide加載出來的圖檔對象了,也就是方法參數中傳過來的Drawable對象。有了這個對象之後你可以使用它進行任意的邏輯操作,這裡我隻是簡單地把它顯示到了ImageView上。

SimpleTarget的實作建立好了,那麼隻需要在加載圖檔的時候将它傳入到into()方法中就可以了。

這裡限于篇幅原因我隻示範了自定義Target的簡單用法,想學習更多相關的内容可以去閱讀 Android圖檔加載架構最全解析(四),玩轉Glide的回調與監聽 。

2. preload()方法

Glide加載圖檔雖說非常智能,它會自動判斷該圖檔是否已經有緩存了,如果有的話就直接從緩存中讀取,沒有的話再從網絡去下載下傳。但是如果我希望提前對圖檔進行一個預加載,等真正需要加載圖檔的時候就直接從緩存中讀取,不想再等待慢長的網絡加載時間了,這該怎麼辦呢?

不用擔心,Glide專門給我們提供了預加載的接口,也就是preload()方法,我們隻需要直接使用就可以了。

preload()方法有兩個方法重載,一個不帶參數,表示将會加載圖檔的原始尺寸,另一個可以通過參數指定加載圖檔的寬和高。

preload()方法的用法也非常簡單,直接使用它來替換into()方法即可,如下所示:

Glide.with(this)
     .load("http://guolin.tech/book.png")
     .preload();           

調用了預加載之後,我們以後想再去加載這張圖檔就會非常快了,因為Glide會直接從緩存當中去讀取圖檔并顯示出來,代碼如下所示:

Glide.with(this)
     .load("http://guolin.tech/book.png")
     .into(imageView);           

1

  • 2
  • 3

3. submit()方法

一直以來,我們使用Glide都是為了将圖檔顯示到界面上。雖然我們知道Glide會在圖檔的加載過程中對圖檔進行緩存,但是緩存檔案到底是存在哪裡的,以及如何去直接通路這些緩存檔案?我們都還不知道。

其實Glide将圖檔加載接口設計成這樣也是希望我們使用起來更加的友善,不用過多去考慮底層的實作細節。但如果我現在就是想要去通路圖檔的緩存檔案該怎麼辦呢?這就需要用到submit()方法了。

submit()方法其實就是對應的Glide 3中的downloadOnly()方法,和preload()方法類似,submit()方法也是可以替換into()方法的,不過submit()方法的用法明顯要比preload()方法複雜不少。這個方法隻會下載下傳圖檔,而不會對圖檔進行加載。當圖檔下載下傳完成之後,我們可以得到圖檔的存儲路徑,以便後續進行操作。

那麼首先我們還是先來看下基本用法。submit()方法有兩個方法重載:

  • submit()
  • submit(int width, int height)

其中submit()方法是用于下載下傳原始尺寸的圖檔,而submit(int width, int height)則可以指定下載下傳圖檔的尺寸。

這裡就以submit()方法來舉例。當調用了submit()方法後會立即傳回一個FutureTarget對象,然後Glide會在背景開始下載下傳圖檔檔案。接下來我們調用FutureTarget的get()方法就可以去擷取下載下傳好的圖檔檔案了,如果此時圖檔還沒有下載下傳完,那麼get()方法就會阻塞住,一直等到圖檔下載下傳完成才會有值傳回。

下面我們通過一個例子來示範一下吧,代碼如下所示:

public void downloadImage() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                String url = "http://www.guolin.tech/book.png";
                final Context context = getApplicationContext();
                FutureTarget<File> target = Glide.with(context)
                        .asFile()
                        .load(url)
                        .submit();
                final File imageFile = target.get();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, imageFile.getPath(), Toast.LENGTH_LONG).show();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}           

這段代碼稍微有一點點長,我帶着大家解讀一下。首先,submit()方法必須要用在子線程當中,因為剛才說了FutureTarget的get()方法是會阻塞線程的,是以這裡的第一步就是new了一個Thread。在子線程當中,我們先擷取了一個Application Context,這個時候不能再用Activity作為Context了,因為會有Activity銷毀了但子線程還沒執行完這種可能出現。

接下來就是Glide的基本用法,隻不過将into()方法替換成了submit()方法,并且還使用了一個asFile()方法來指定加載格式。submit()方法會傳回一個FutureTarget對象,這個時候其實Glide已經開始在背景下載下傳圖檔了,我們随時都可以調用FutureTarget的get()方法來擷取下載下傳的圖檔檔案,隻不過如果圖檔還沒下載下傳好線程會暫時阻塞住,等下載下傳完成了才會把圖檔的File對象傳回。

最後,我們使用runOnUiThread()切回到主線程,然後使用Toast将下載下傳好的圖檔檔案路徑顯示出來。

現在重新運作一下代碼,效果如下圖所示。

Android圖檔加載架構最全解析(八),帶你全面了解Glide 4的用法

這樣我們就能清晰地看出來圖檔完整的緩存路徑是什麼了。

4. listener()方法

其實listener()方法的作用非常普遍,它可以用來監聽Glide加載圖檔的狀态。舉個例子,比如說我們剛才使用了preload()方法來對圖檔進行預加載,但是我怎樣确定預加載有沒有完成呢?還有如果Glide加載圖檔失敗了,我該怎樣調試錯誤的原因呢?答案都在listener()方法當中。

下面來看下listener()方法的基本用法吧,不同于剛才幾個方法都是要替換into()方法的,listener()是結合into()方法一起使用的,當然也可以結合preload()方法一起使用。最基本的用法如下所示:

Glide.with(this)
     .load("http://www.guolin.tech/book.png")
     .listener(new RequestListener<Drawable>() {
         @Override
         public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
             return false;
         }

         @Override
         public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
             return false;
         }
     })
     .into(imageView);           

這裡我們在into()方法之前串接了一個listener()方法,然後實作了一個RequestListener的執行個體。其中RequestListener需要實作兩個方法,一個onResourceReady()方法,一個onLoadFailed()方法。從方法名上就可以看出來了,當圖檔加載完成的時候就會回調onResourceReady()方法,而當圖檔加載失敗的時候就會回調onLoadFailed()方法,onLoadFailed()方法中會将失敗的GlideException參數傳進來,這樣我們就可以定位具體失敗的原因了。

沒錯,listener()方法就是這麼簡單。不過還有一點需要處理,onResourceReady()方法和onLoadFailed()方法都有一個布爾值的傳回值,傳回false就表示這個事件沒有被處理,還會繼續向下傳遞,傳回true就表示這個事件已經被處理掉了,進而不會再繼續向下傳遞。舉個簡單點的例子,如果我們在RequestListener的onResourceReady()方法中傳回了true,那麼就不會再回調Target的onResourceReady()方法了。

關于回調與監聽的内容就講這麼多吧,如果想要學習更多深入的内容以及源碼解析,還是請參考這篇文章 Android圖檔加載架構最全解析(四),玩轉Glide的回調與監聽 。

圖檔變換

圖檔變換的意思就是說,Glide從加載了原始圖檔到最終展示給使用者之前,又進行了一些變換處理,進而能夠實作一些更加豐富的圖檔效果,如圖檔圓角化、圓形化、模糊化等等。

添加圖檔變換的用法非常簡單,我們隻需要在RequestOptions中串接transforms()方法,并将想要執行的圖檔變換操作作為參數傳入transforms()方法即可,如下所示:

RequestOptions options = new RequestOptions()
        .transforms(...);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);           

至于具體要進行什麼樣的圖檔變換操作,這個通常都是需要我們自己來寫的。不過Glide已經内置了幾種圖檔變換操作,我們可以直接拿來使用,比如CenterCrop、FitCenter、CircleCrop等。

但所有的内置圖檔變換操作其實都不需要使用transform()方法,Glide為了友善我們使用直接提供了現成的API:

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

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

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

當然,這些内置的圖檔變換API其實也隻是對transform()方法進行了一層封裝而已,它們背後的源碼仍然還是借助transform()方法來實作的。

這裡我們就選擇其中一種内置的圖檔變換操作來示範一下吧,circleCrop()方法是用來對圖檔進行圓形化裁剪的,我們動手試一下,代碼如下所示:

String url = "http://guolin.tech/book.png";
RequestOptions options = new RequestOptions()
        .circleCrop();
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);           

重新運作一下程式并點選加載圖檔按鈕,效果如下圖所示。

Android圖檔加載架構最全解析(八),帶你全面了解Glide 4的用法

可以看到,現在展示的圖檔是對原圖進行圓形化裁剪後得到的圖檔。

當然,除了使用内置的圖檔變換操作之外,我們完全可以自定義自己的圖檔變換操作。理論上,在對圖檔進行變換這個步驟中我們可以進行任何的操作,你想對圖檔怎麼樣都可以。包括圓角化、圓形化、黑白化、模糊化等等,甚至你将原圖檔完全替換成另外一張圖都是可以的。

不過由于這部分内容相對于Glide 3沒有任何的變化,是以就不再重複進行講解了。想學習自定義圖檔變換操作的朋友們可以參考這篇文章 Android圖檔加載架構最全解析(五),Glide強大的圖檔變換功能 。

關于圖檔變換,最後我們再來看一個非常優秀的開源庫,glide-transformations。它實作了很多通用的圖檔變換效果,如裁剪變換、顔色變換、模糊變換等等,使得我們可以非常輕松地進行各種各樣的圖檔變換。

glide-transformations的項目首頁位址是 https://github.com/wasabeef/glide-transformations 。

下面我們就來體驗一下這個庫的強大功能吧。首先需要将這個庫引入到我們的項目當中,在app/build.gradle檔案當中添加如下依賴:

dependencies {
    implementation \'jp.wasabeef:glide-transformations:3.0.1\'
}           

我們可以對圖檔進行單個變換處理,也可以将多種圖檔變換疊加在一起使用。比如我想同時對圖檔進行模糊化和黑白化處理,就可以這麼寫:

String url = "http://guolin.tech/book.png";
RequestOptions options = new RequestOptions()
        .transforms(new BlurTransformation(), new GrayscaleTransformation());
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);           

可以看到,同時執行多種圖檔變換的時候,隻需要将它們都傳入到transforms()方法中即可。現在重新運作一下程式,效果如下圖所示。

Android圖檔加載架構最全解析(八),帶你全面了解Glide 4的用法

當然,這隻是glide-transformations庫的一小部分功能而已,更多的圖檔變換效果你可以到它的GitHub項目首頁去學習。

自定義子產品

自定義子產品屬于Glide中的進階功能,同時也是難度比較高的一部分内容。

這裡我不可能在這一篇文章中将自定義子產品的内容全講一遍,限于篇幅的限制我隻能講一講Glide 4中變化的這部分内容。關于Glide自定義子產品的全部内容,請大家去參考 Android圖檔加載架構最全解析(六),探究Glide的自定義子產品功能 這篇文章。

自定義子產品功能可以将更改Glide配置,替換Glide元件等操作獨立出來,使得我們能輕松地對Glide的各種配置進行自定義,并且又和Glide的圖檔加載邏輯沒有任何交集,這也是一種低耦合程式設計方式的展現。下面我們就來學習一下自定義子產品要如何實作。

首先定義一個我們自己的子產品類,并讓它繼承自AppGlideModule,如下所示:

@GlideModule
public class MyAppGlideModule extends AppGlideModule {

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

    }

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

    }

}           

可以看到,在MyAppGlideModule類當中,我們重寫了applyOptions()和registerComponents()方法,這兩個方法分别就是用來更改Glide配置以及替換Glide元件的。

注意在MyAppGlideModule類在上面,我們加入了一個@GlideModule的注解,這是Gilde 4和Glide 3最大的一個不同之處。在Glide 3中,我們定義了自定義子產品之後,還必須在AndroidManifest.xml檔案中去注冊它才能生效,而在Glide 4中是不需要的,因為@GlideModule這個注解已經能夠讓Glide識别到這個自定義子產品了。

這樣的話,我們就将Glide自定義子產品的功能完成了。後面隻需要在applyOptions()和registerComponents()這兩個方法中加入具體的邏輯,就能實作更改Glide配置或者替換Glide元件的功能了。詳情還是請參考 Android圖檔加載架構最全解析(六),探究Glide的自定義子產品功能 這篇文章,這裡就不再展開讨論了。

使用Generated API

Generated API是Glide 4中全新引入的一個功能,它的工作原理是使用注解處理器 (Annotation Processor) 來生成出一個API,在Application子產品中可使用該流式API一次性調用到RequestBuilder,RequestOptions和內建庫中所有的選項。

這麼解釋有點拗口,簡單點說,就是Glide 4仍然給我們提供了一套和Glide 3一模一樣的流式API接口。畢竟有些人還是覺得Glide 3的API更好用一些,比如說我。

Generated API對于熟悉Glide 3的朋友來說那是再簡單不過了,基本上就是和Glide 3一模一樣的用法,隻不過需要把Glide關鍵字替換成GlideApp關鍵字,如下所示:

GlideApp.with(this)
        .load(url)
        .placeholder(R.drawable.loading)
        .error(R.drawable.error)
        .skipMemoryCache(true)
        .diskCacheStrategy(DiskCacheStrategy.NONE)
        .override(Target.SIZE_ORIGINAL)
        .circleCrop()
        .into(imageView);           

不過,有可能你的IDE中會提供找不到GlideApp這個類。這個類是通過編譯時注解自動生成的,首先確定你的代碼中有一個自定義的子產品,并且給它加上了@GlideModule注解,也就是我們在上一節所講的内容。然後在Android Studio中點選菜單欄Build -> Rebuild Project,GlideApp這個類就會自動生成了。

當然,Generated API所能做到的并不隻是這些而已,它還可以對現有的API進行擴充,定制出任何屬于你自己的API。

下面我來具體舉個例子,比如說我們要求項目中所有圖檔的緩存政策全部都要緩存原始圖檔,那麼每次在使用Glide加載圖檔的時候,都去指定diskCacheStrategy(DiskCacheStrategy.DATA)這麼長長的一串代碼,确實是讓人比較心煩。這種情況我們就可以去定制一個自己的API了。

定制自己的API需要借助@GlideExtension和@GlideOption這兩個注解。建立一個我們自定義的擴充類,代碼如下所示:

@GlideExtension
public class MyGlideExtension {

    private MyGlideExtension() {

    }

    @GlideOption
    public static void cacheSource(RequestOptions options) {
        options.diskCacheStrategy(DiskCacheStrategy.DATA);
    }

}           

這裡我們定義了一個MyGlideExtension類,并且給加上了一個@GlideExtension注解,然後要将這個類的構造函數聲明成private,這都是必須要求的寫法。

接下來就可以開始自定義API了,這裡我們定義了一個cacheSource()方法,表示隻緩存原始圖檔,并給這個方法加上了@GlideOption注解。注意自定義API的方法都必須是靜态方法,而且第一個參數必須是RequestOptions,後面你可以加入任意多個你想自定義的參數。

在cacheSource()方法中,我們仍然還是調用的diskCacheStrategy(DiskCacheStrategy.DATA)方法,是以說cacheSource()就是一層簡化API的封裝而已。

然後在Android Studio中點選菜單欄Build -> Rebuild Project,神奇的事情就會發生了,你會發現你已經可以使用這樣的語句來加載圖檔了:

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

有了這個強大的功能之後,我們使用Glide就能變得更加靈活了。

結束語

這樣我們基本上就将Glide 4的所有重要内容都介紹完了,如果你以前非常熟悉Glide 3的話,看完這篇文章之後相信你已經能夠熟練使用Glide 4了。而如果你以前并未接觸過Glide,僅僅隻看這一篇文章可能了解得還不夠深入,建議最好還是把前面的七篇文章也去通讀一下,這樣你才能成為一名Glide好手。

我翻了一下曆史記錄,在今年的3月21号發了這個系列的第一篇文章,用了10個月的時間終于把這個系列全部更新完了。當時承諾的是寫八篇文章,如今兌現了承諾,也算是有始有終吧。未來我希望能繼續給大家帶來更好的技術文章,不過這個系列就到此為止了。也感謝有耐心的朋友能夠看到最後,能堅持看完的人,你們都和我一樣棒。