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

如今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教程。緩存

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

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

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

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

dependencies {
    compile 'com.github.bumptech.glide:glide:3.7.0'
}

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

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

<uses-permission android:name="android.permission.INTERNET" /> 1 就是這麼簡單,而後咱們就能夠自由地使用Glide中的任意功能了。

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

http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg 1 而後咱們想要在程序當中去加載這張圖片。

那麼首先打開項目的佈局文件,在佈局當中加入一個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](https://my.oschina.net/u/1162528)
    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對象
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地址:

  1. 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進階技術大綱及進階資料,及面試題集

想學習更多Android知識,請加入Android技術開發企鵝交流 7520 16839

進羣與大牛們一塊兒討論,還可獲取Android高級架構資料、源碼、筆記、視頻

包括 高級UI、Gradle、RxJava、小程序、Hybrid、移動架構、React Native、性能優化等全面的Android高級實踐技術講解性能優化架構思惟導圖,和BATJ面試題及答案!

羣裏免費分享給有須要的朋友,但願可以幫助一些在這個行業發展迷茫的,或者想系統深刻提高以及困於瓶頸的朋友,在網上博客論壇等地方少花些時間找資料,把有限的時間,真正花在學習上,因此我在這免費分享一些架構資料及給你們。但願在這些資料中都有你須要的內容。

相關文章
相關標籤/搜索