1. 程式人生 > >優化載入gif動畫

優化載入gif動畫

專案開發中都會用到各種載入進度的動畫,有幾下幾種方式可以參考:
1、使用animation-list一幀一幀的播放出來
最簡單的方式是將設計同學給的png序列直接放到一個 animation-list中
這裡寫圖片描述
然後直接,放在設定為一個ImageView就可以了
這裡寫圖片描述
這個方法如果圖片過多就會導致oom;
2、使用一個執行緒來讀取PNG序列,另外一個執行緒去播放讀取出來的PNG序列,那麼有一些問題我們要去面對:
a、一個執行緒來讀,一個執行緒寫,讀PNG的執行緒寫,播PNG的執行緒讀,哎呀,有點拗口~~,不過很顯然,這是一個《生產者-消費者模型》,那麼問題是使用什麼存放讀取好的bitmap呢,使用BlockingQueue 吧,為什麼要使用BlockingQueue,如果不懂,請點選這裡,還能不能使用別的,當然,有,而且還不止一個,感興趣可以去這個包下java.util.concurrent探索下。

阻塞佇列(BlockingQueue)是一個支援兩個附加操作的佇列。這兩個附加的操作是:在佇列為空時,獲取元素的執行緒會等待佇列變為非空。當佇列滿時,儲存元素的執行緒會等待佇列可用。阻塞佇列常用於生產者和消費者的場景,生產者是往佇列裡新增元素的執行緒,消費者是從佇列裡拿元素的執行緒。阻塞佇列就是生產者存放元素的容器,而消費者也只從容器裡拿元素。

b、不是怕OOM嗎?那麼,這個方案是否可以解決OOM呢?但是顯然是肯定的了。為什麼這麼說,都到了這種粒度了,OOM當然是可以解決。

b1、首先,我們可以拿到當前的最大記憶體Runtime.getRuntime().maxMemory(),和當前的可用記憶體Runtime.getRuntime().freeMemory();
因此,結合BitmapFactory.Options,的這個inJustDecodeBounds屬性,你完全可以判斷是否還有足夠的記憶體載入更多的bitmap。

b2、其次,維護一個currentSize,記錄解析到記憶體測bitmap佔用的記憶體,每讀一張,currentSize+讀出來的bitmap佔用的記憶體,currentSize顯然是變動的,播放完的bitmap請補上一刀,currentSize - 剛剛播放完的bitmap。
這裡寫圖片描述
3、多執行緒讀取PNG
這裡寫圖片描述
當然,多執行緒併發去讀取,怎樣能夠保證讀取出來的某個PNG是不是應該在它正確的位置呢?
很顯然要做到這一點,就需要將png的序號帶入到讀取執行緒中。讀取執行緒讀取完畢之後,去問一個manger,大哥,有比我小的讀取執行緒還沒有提交他拿到的bitmap嗎?大哥告訴你還有,那對不起,你乖乖等一會吧,wait(關鍵字),對麼?如果大哥告訴你沒有,你丫就是序號最小的那個哦,那你就把bitmap交給BlockingQuene吧,然而自己就完成光榮使命了。

可問題是,如果你在wait,誰來叫醒你呢?大哥說,他來notify,大哥收到最小的序號的提交的bitmap,等等,(上面說錯了,最小的需要把bitmap交給大哥來提交,),將bitmap交給BlockingQuene,然後大哥此時通知所有讀取執行緒的小弟們,大夥趕緊來交作業了,如是此時你單身10年的左手終於搶到了“鎖”,如是,你把你的作業bitmap交給了大哥了。

還有一種思路就是對號入座:這種情況表達0123已經提交給BlockingQuene,5先完成了,然後3完成了,4沒完成,此時大哥會吧3提交給BlockingQuene對嗎?顯然是,情況還有很多,,可以自己腦補一下,總之,這麼做,讀取執行緒只要讀取完畢,把作業交給大哥就好,不用等待大哥說你是最小的,才讓你提交,是嗎?
這裡寫圖片描述
okay,整體思路就是這樣,看看效果吧,go。。。
這裡寫圖片描述
4、單執行緒讀取,重複利用釋放的bitmap資源
google給了我這麼一個答案:
https://developer.android.com/training/displaying-bitmaps/manage-memory.html#recycle
這裡寫圖片描述
options有這麼一個引數 ,可以重用一個bitmap的記憶體去存放解析出另外一個新的bitmap,但是有一定的要求:
4.4以上,只需要old bitmap位元組數比將要載入的bitmap所需的位元組數大,但是低於4.4,要滿足和待載入bitmap長寬畫素一致即可 (更加苛刻)。

而我們的png序列,每張圖片都是一樣大小,顯然,符合這個所有特性(長寬一致)。

如是,有多了集合去儲存即將釋放的bitmap,用來重用。
這裡寫圖片描述
測試結果:
這裡寫圖片描述

這裡寫圖片描述