Android圖片加載框架最全解析(一),Glide的基本用法

本文同步發表於個人微信公衆號,掃一掃文章底部的二維碼或在微信搜索 郭霖 便可關注,天天都有文章更新。html

如今 Android 上的圖片加載框架很是成熟,從最先的老牌圖片加載框架 UniversalImageLoader,到後來 Google 推出的 Volley,再到後來的新興軍 Glide 和 Picasso,固然還有 Facebook 的 Fresco。每個都很是穩定,功能也都十分強大。可是它們的使用場景基本都是重合的,也就是說咱們基本只須要選擇其中一個來進行學習和使用就足夠了,每個框架都嘗試去掌握的話則有些浪費時間。android

在這幾個框架當中,我對 Volley 和 Glide 研究得比較深刻,對 UniversalImageLoader、Picasso 和 Fresco 都只是有一些基本的瞭解。從易用性上來說,Glide 和 Picasso 應該都是完勝其餘框架的,這兩個框架都實在是太簡單好用了,大多數狀況下加載圖片都是一行代碼就能解決的,而 UniversalImageLoader 和 Fresco 則在這方面略遜一些。git

那麼再拿 Glide 和 Picasso 對比呢,首先這兩個框架的用法很是類似,但其實它們各有特點。Picasso 比 Glide 更加簡潔和輕量,Glide 比 Picasso 功能更爲豐富。以前已經有人對這兩個框架進行過全方面的對比,你們若是想了解更多的話能夠去參考一下 這篇文章github

總之,沒有最好的框架,只有最適合本身的框架。通過多方面對比以後,我仍是決定選擇了 Glide 來進行研究,而且這也是 Google 官方推薦的圖片加載框架。緩存

說實話,關於 Glide 的文章我已經籌備了很久,去年這個時候原本就打算要寫了,可是一直都沒有動筆。由於去年個人大部分時間都放在了寫《第二行代碼》上面,只能用碎片時間來寫寫博客,可是 Glide 的難度遠超出了我用碎片時間所能掌握的難度。固然,這裏我說的是對它的源碼進行解析的難度,不是使用上的難度,Glide 的用法是很簡單的。因此,我以爲去年我寫很差 Glide 這個題材的文章,也就一直拖到了今年。微信

而如今,我花費了大量的精力去研究 Glide 的源碼和各類用法,相信如今已經能夠將它很是好地掌握了,所以我準備將我掌握的這些知識整理成一個新的系列,幫忙你們更好地學習 Glide。這個 Glide 系列大概會有 8 篇左右文章,預計花半年時間寫完,將會包括 Glide 的基本用法、源碼解析、高級用法、功能擴展等內容,可能會是目前互聯網上最詳盡的 Glide 教程。markdown

那麼本篇文章是這個系列的第一篇文章,咱們先來了解一下 Glide 的基本用法吧。網絡

Glide 是一款由 Bump Technologies 開發的圖片加載框架,使得咱們能夠在 Android 平臺上以極度簡單的方式加載和展現圖片。app

目前,Glide 最新的穩定版本是 3.7.0,雖然 4.0 已經推出 RC 版了,可是暫時問題還比較多。所以,咱們這個系列的博客都會使用 Glide 3.7.0 版原本進行講解,這個版本的 Glide 至關成熟和穩定。框架

要想使用 Glide,首先須要將這個庫引入到咱們的項目當中。新建一個 GlideTest 項目,而後在 app/build.gradle 文件當中添加以下依賴:

dependencies {
    compile 'com.github.bumptech.glide:glide:3.7.0'
}
複製代碼

若是你還在使用 Eclipse,能夠點擊 這裏 下載 Glide 的 jar 包。

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

<uses-permission android: />
複製代碼

就是這麼簡單,而後咱們就能夠自由地使用 Glide 中的任意功能了。

如今咱們就來嘗試一下如何使用 Glide 來加載圖片吧。好比這是必應上一張首頁美圖的地址:

http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg
複製代碼

而後咱們想要在程序當中去加載這張圖片。

那麼首先打開項目的佈局文件,在佈局當中加入一個 Button 和一個 ImageView,以下所示:

<?xml version="1.0" encoding="utf-8"?>
<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://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg";
        Glide.with(this).load(url).into(imageView);
    }

}
複製代碼

沒錯,就是這麼簡單。如今咱們來運行一下程序,效果以下圖所示:

能夠看到,一張網絡上的圖片已經被成功下載,而且展現到 ImageView 上了。

而咱們到底作了什麼?實際上核心的代碼就只有這一行而已:

Glide.with(this).load(url).into(imageView);
複製代碼

千萬不要小看這一行代碼,實際上僅僅就這一行代碼,你已經能夠作很是很是多的事情了,包括加載網絡上的圖片、加載手機本地的圖片、加載應用資源中的圖片等等。

下面咱們就來詳細解析一下這行代碼。

首先,調用 Glide.with() 方法用於建立一個加載圖片的實例。with() 方法能夠接收 Context、Activity 或者 Fragment 類型的參數。也就是說咱們選擇的範圍很是廣,無論是在 Activity 仍是 Fragment 中調用 with() 方法,均可以直接傳 this。那若是調用的地方既不在 Activity 中也不在 Fragment 中呢?也不要緊,咱們能夠獲取當前應用程序的 ApplicationContext,傳入到 with() 方法當中。注意 with() 方法中傳入的實例會決定 Glide 加載圖片的生命週期,若是傳入的是 Activity 或者 Fragment 的實例,那麼當這個 Activity 或 Fragment 被銷燬的時候,圖片加載也會中止。若是傳入的是 ApplicationContext,那麼只有當應用程序被殺掉的時候,圖片加載纔會中止。

接下來看一下 load() 方法,這個方法用於指定待加載的圖片資源。Glide 支持加載各類各樣的圖片資源,包括網絡圖片、本地圖片、應用資源、二進制流、Uri 對象等等。所以 load() 方法也有不少個方法重載,除了咱們剛纔使用的加載一個字符串網址以外,你還能夠這樣使用 load() 方法:

File file = new File(getExternalCacheDir() + "/image.jpg");
Glide.with(this).load(file).into(imageView);


int resource = R.drawable.image;
Glide.with(this).load(resource).into(imageView);


byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);


Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
複製代碼

最後看一下 into() 方法,這個方法就很簡單了,咱們但願讓圖片顯示在哪一個 ImageView 上,把這個 ImageView 的實例傳進去就能夠了。固然,into() 方法不只僅是隻能接收 ImageView 類型的參數,還支持不少更豐富的用法,不過那個屬於高級技巧,咱們會在後面的文章當中學習。

那麼回顧一下 Glide 最基本的使用方式,其實就是關鍵的三步走:先 with(),再 load(),最後 into()。熟記這三步,你就已經入門 Glide 了。

如今咱們來學一些 Glide 的擴展內容。其實剛纔所學的三步走就是 Glide 最核心的東西,而咱們後面所要學習的全部東西都是在這個三步走的基礎上不斷進行擴展而已。

觀察剛纔加載網絡圖片的效果,你會發現,點擊了 Load Image 按鈕以後,要稍微等一會圖片纔會顯示出來。這其實很容易理解,由於從網絡上下載圖片原本就是須要時間的。那麼咱們有沒有辦法再優化一下用戶體驗呢?固然能夠,Glide 提供了各類各樣很是豐富的 API 支持,其中就包括了佔位圖功能。

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

下面咱們就來學習一下 Glide 佔位圖功能的使用方法,首先我事先準備好了一張 loading.jpg 圖片,用來做爲佔位圖顯示。而後修改 Glide 加載部分的代碼,以下所示:

Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading)
     .into(imageView);
複製代碼

沒錯,就是這麼簡單。咱們只是在剛纔的三步走之間插入了一個 placeholder() 方法,而後將佔位圖片的資源 id 傳入到這個方法中便可。另外,這個佔位圖的用法其實也演示了 Glide 當中絕大多數 API 的用法,其實就是在 load() 和 into() 方法之間串接任意想添加的功能就能夠了。

不過若是你如今從新運行一下代碼並點擊 Load Image,極可能是根本看不到佔位圖效果的。由於 Glide 有很是強大的緩存機制,咱們剛纔加載那張必應美圖的時候 Glide 自動就已經將它緩存下來了,下次加載的時候將會直接從緩存中讀取,不會再去網絡下載了,於是加載的速度很是快,因此佔位圖可能根原本不及顯示。

所以這裏咱們還須要稍微作一點修改,來讓佔位圖能有機會顯示出來,修改代碼以下所示:

Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);
複製代碼

能夠看到,這裏串接了一個 diskCacheStrategy() 方法,並傳入 DiskCacheStrategy.NONE 參數,這樣就能夠禁用掉 Glide 的緩存功能。

關於 Glide 緩存方面的內容咱們將會在後面的文章進行詳細的講解,這裏只是爲了測試佔位圖功能而加的一個額外配置,暫時你只須要知道禁用緩存必須這麼寫就能夠了。

如今從新運行一下代碼,效果以下圖所示:

能夠看到,當點擊 Load Image 按鈕以後會當即顯示一張佔位圖,而後等真正的圖片加載完成以後會將佔位圖替換掉。

固然,這只是佔位圖的一種,除了這種加載佔位圖以外,還有一種異常佔位圖。異常佔位圖就是指,若是由於某些異常狀況致使圖片加載失敗,好比說手機網絡信號很差,這個時候就顯示這張異常佔位圖。

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

Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);
複製代碼

很簡單,這裏又串接了一個 error() 方法就能夠指定異常佔位圖了。

如今你能夠將圖片的 url 地址修改爲一個不存在的圖片地址,或者乾脆直接將手機的網絡給關了,而後從新運行程序,效果以下圖所示:

這樣咱們就把 Glide 提供的佔位圖功能都掌握了。

咱們還須要再瞭解一下 Glide 另一個強大的功能,那就是 Glide 是支持加載 GIF 圖片的。這一點確實很是牛逼,由於相比之下 Jake Warton 曾經明確表示過,Picasso 是不會支持加載 GIF 圖片的。

而使用 Glide 加載 GIF 圖並不須要編寫什麼額外的代碼,Glide 內部會自動判斷圖片格式。好比這是一張 GIF 圖片的 URL 地址:

http://p1.pstatp.com/large/166200019850062839d3
複製代碼

咱們只須要將剛纔那段加載圖片代碼中的 URL 地址替換成上面的地址就能夠了,如今從新運行一下代碼,效果以下圖所示:

也就是說,無論咱們傳入的是一張普通圖片,仍是一張 GIF 圖片,Glide 都會自動進行判斷,而且能夠正確地把它解析並展現出來。

可是若是我想指定圖片的格式該怎麼辦呢?就好比說,我但願加載的這張圖必須是一張靜態圖片,我不須要 Glide 自動幫我判斷它究竟是靜圖仍是 GIF 圖。

想實現這個功能仍然很是簡單,咱們只須要再串接一個新的方法就能夠了,以下所示:

Glide.with(this)
     .load(url)
     .asBitmap()
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);
複製代碼

能夠看到,這裏在 load() 方法的後面加入了一個 asBitmap() 方法,這個方法的意思就是說這裏只容許加載靜態圖片,不須要 Glide 去幫咱們自動進行圖片格式的判斷了。

如今從新運行一下程序,效果以下圖所示:

因爲調用了 asBitmap() 方法,如今 GIF 圖就沒法正常播放了,而是會在界面上顯示第一幀的圖片。

那麼相似地,既然咱們能強制指定加載靜態圖片,就也能強制指定加載動態圖片。好比說咱們想要實現必須加載動態圖片的功能,就能夠這樣寫:

Glide.with(this)
     .load(url)
     .asGif()
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);
複製代碼

這裏調用了 asGif() 方法替代了 asBitmap() 方法,很好理解,相信不用我多作什麼解釋了。

那麼既然指定了只容許加載動態圖片,若是咱們傳入了一張靜態圖片的 URL 地址又會怎麼樣呢?試一下就知道了,將圖片的 URL 地址改爲剛纔的必應美圖,而後從新運行代碼,效果以下圖所示。

沒錯,若是指定了只能加載動態圖片,而傳入的圖片倒是一張靜圖的話,那麼結果天然就只有加載失敗嘍。

實際上,使用 Glide 在絕大多數狀況下咱們都是不須要指定圖片大小的。

在學習本節內容以前,你可能還須要先了解一個概念,就是咱們平時在加載圖片的時候很容易會形成內存浪費。什麼叫內存浪費呢?好比說一張圖片的尺寸是 10001000 像素,可是咱們界面上的 ImageView 可能只有 200200 像素,這個時候若是你不對圖片進行任何壓縮就直接讀取到內存中,這就屬於內存浪費了,由於程序中根本就用不到這麼高像素的圖片。

關於圖片壓縮這方面,我以前也翻譯過 Android 官方的一篇文章,感興趣的朋友能夠去閱讀一下 Android 高效加載大圖、多圖解決方案,有效避免程序 OOM

而使用 Glide,咱們就徹底不用擔憂圖片內存浪費,甚至是內存溢出的問題。由於 Glide 歷來都不會直接將圖片的完整尺寸所有加載到內存中,而是用多少加載多少。Glide 會自動判斷 ImageView 的大小,而後只將這麼大的圖片像素加載到內存當中,幫助咱們節省內存開支。

固然,Glide 也並無使用什麼神奇的魔法,它內部的實現原理其實就是上面那篇文章當中介紹的技術,所以掌握了最基本的實現原理,你也能夠本身實現一套這樣的圖片壓縮機制。

也正是由於 Glide 是如此的智能,因此剛纔在開始的時候我就說了,在絕大多數狀況下咱們都是不須要指定圖片大小的,由於 Glide 會自動根據 ImageView 的大小來決定圖片的大小。

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

Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .override(100, 100)
     .into(imageView);
複製代碼

仍然很是簡單,這裏使用 override() 方法指定了一個圖片的尺寸,也就是說,Glide 如今只會將圖片加載成 100*100 像素的尺寸,而不會管你的 ImageView 的大小是多少了。

好了,今天是咱們這個 Glide 系列的第一篇文章,寫了這麼多內容已經算是挺不錯的了。如今你已經瞭解了 Glide 的基本用法,固然也是一些最經常使用的用法。下一篇文章當中,咱們會嘗試去分析 Glide 的源碼,研究一下在這些基本用法的背後,Glide 到底執行了什麼神奇的操做,可以使得咱們加載圖片變得這麼簡單?感興趣的朋友請繼續閱讀 Android 圖片加載框架最全解析(二),從源碼的角度理解 Glide 的執行流程

相關文章
相關標籤/搜索