Glide載入gif圖片優化

冬榮發表於1970-01-01

Glide是一個快速高效的Android圖片載入庫,注重於平滑的滾動提供了易用的API,高效能、可擴充套件的圖片解碼管道以及自動的資源池技術。這個庫被廣泛運用在google的開源專案中,包括2014年的google I/O大會上釋出的官方app。但是大家有沒有發現當使用glide載入大的gif圖片,或者載入很多個gif的列表時會有明顯示卡頓,CPU和記憶體佔用量很高。我們知道glide優勢是擁有一套圖片生命週期的維護,但是在載入gif的時候glide效率明顯比不過android-gif-drawable,原因就是後者是在native層進行載入的,使用的是giflib庫。那我們是否可以保留二者的優勢,取長補短呢?今天作者給大家分享一下如何對glide進行優化,希望能給大家帶來幫助。

今天我們分別用優化前和優化後的glide框架來載入gif圖片,然後使用profile來檢視CPU和記憶體佔用情況,並且進行一個對比。對比結果如下圖,CPU和記憶體佔用明顯降低。

優化前:

Glide載入gif圖片優化

優化後:

Glide載入gif圖片優化

如何進行優化?

想要進行優化,我們就必須分析glide載入gif卡頓的原因。glide載入gif是通過java層來載入,所以效能肯定是要大打折扣。這裡我們可以想辦法將gif載入的過程放到native中,於是我們想到了giflib。但是giflib是C庫,使用起來非常不方便,於是谷歌就封裝好giflib,提供了使用java就可以呼叫到的API,這就是frameSequence。

步驟一:生成so檔案

首先下載好framesequence和giflib包,然後在framesequence目錄中建立external目錄,並將giflib解碼目錄複製到該目錄下,然後執行ndk-build,最後將生成的so放到jnilibs目錄下。


步驟二:建立FrameSequence物件以及FrameSequenceDrawable物件

frameSequence的sample專案中已有了,直接複製過來就可以使用了

Glide載入gif圖片優化

Glide載入gif圖片優化

步驟三 自定義glide資源解碼器

經過以上三步,準備工作已做好,接下來就是要將frameSequence和glide進行結合了。frameSequence負責圖片解碼,glide負責下載和生命週期維護。想要替換解碼部分,就必須瞭解glide的實現了。glide載入過程分為載入和解碼兩大步驟,其中載入過程是通過模型載入器(ModelLoader)來實現,而解碼過程是通過資源解碼器(ResourceDecoder)來實現的,所以我們要做的就是將glide的ResourceDecoder中預設的解碼操作替換成frameSequence。

Glide載入gif圖片優化

那應該怎麼給glide註冊一個資源解碼器呢?

其實Glide v4 使用 APT(註解處理器) 來生成出一個 API。可以為 Generated API 擴充套件自定義選項,我們可以繼承AppGlideModule,重寫registerComponents方法,在裡面新增一個gif解碼器。注意需要使用@GlideModule進行註解,新增完該註解後在編譯時會自動生成一些類。這裡將glide.getBitmapPool傳遞到解碼器中是為了bitmap的複用

Glide載入gif圖片優化

寫好這個類後,我們直接編譯一下專案,就會發現glide自動為我們生成了很多的java檔案,如下圖:

Glide載入gif圖片優化

接下來我們再看看核心的解碼器需要做什麼操作

Glide載入gif圖片優化

我們可以看到,這裡面首先構造方法接收到了glide序號產生器傳過來的bitmapPool,用在decode方法中進行復用。在handles中返回true代表我們的解碼器直接處理了該任務,然後在最核心的decode方法中,我們初始化了一個FrameSequenceDrawable並且返回。這裡由於返回值是Resource,所以我們不得不包了一層自定義的Resource子類。

什麼是記憶體抖動?

記憶體抖動是指頻繁的申請記憶體,然後又回收記憶體,使得記憶體使用很不穩定,萬一沒有回收好就會產生記憶體洩漏。

什麼是記憶體碎片?

在連續的記憶體空間中,有一部分記憶體被使用,另一部分已經被回收,所以導致目前可使用的記憶體空間不是連續的,而不是連續的將不可以同時一次性拿來使用。比如記憶體空間是1 2 3 4個格子,其中1和3是正在使用的記憶體,2和4是空閒記憶體,此時如果2和4單個的記憶體空間不夠,那麼就需要同時申請到2和4,由於2和4的空間不連續,所以就會導致申請失敗。其中2和4這2個記憶體空間就被稱為記憶體碎片,太小的碎片將無法使用,非常影響效率。

有關bitmapPool的複用

這裡我貼出谷歌官方的2張圖,第一張是複用前,每個圖片都分配一個記憶體空間。第二張是複用後,所有圖片複用同一個bitmap空間,減少記憶體使用。

Glide載入gif圖片優化

Glide載入gif圖片優化

步驟四 自定義解碼器的使用

最後,我們通過呼叫之前通過APT生成的GlideApp,然後通過as方法傳入FSDrawable的方式來呼叫我們自定義的解碼器

Glide載入gif圖片優化


是否還能優化一下呼叫流程?

以上實際的載入優化已經完成,但是我們發現呼叫的時候需要as方法傳入FrameSequenceDrawable,寫起來很不方便,希望能改成類似於asgif這種呼叫方法,這個又需要使用到glide自帶的APT技術了,換句話說我們需要使用一個註解。這裡我們新建一個類,類名隨意,然後使用一下@GlideExtension註解,這裡需要注意的是,類中必須要有一個私有無參構造方法,不然glide是會報錯的。然後我們自定義一個asGif2方法,使用@GlideType進行標註,在裡面就呼叫requestBuilder.apply方法就好了。寫完以後要編譯一下,讓glide通過註解生成該方法,這樣就可以直接呼叫了。

Glide載入gif圖片優化

Glide載入gif圖片優化


總結:glide優化其實就是指利用glide可以自定義解碼器的特點,我們自己來定義並且註冊了一個資源解碼器,其中指定使用FrameSequence來進行解碼,核心其實就是使用的giflib庫。利用native層來實現資源的解碼,提升了載入效率,其中我們還特意對bitmap進行了複用。然後為了呼叫方便,我們將呼叫方式從as改為asGif2,呼叫更加清晰明瞭。


相關文章