Bitmap在Android中廣泛應用,尤其是圖檔處理時。看下今天Agenda:
位圖的基本概念
Bitmap與檔案格式
Bitmap記憶體占用優化
BitmapShader的應用
recycle的實踐
我們先抛開Bitmap在Android上的實作不談,先看看Bitmap在實體世界的具體含義
計算機圖形學上的概念
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiInVGcq5SMxYjM0UWY1MWZhNjN4YjN0YmNhV2NzEDMmVmYkZ2M28CXwIzLcVDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL3M3Lc9CX6MHc0RHaiojIsJye.jpeg)
根據位深度,可将位圖分為1、4、8、16、24及32位圖像等。每個像素使用的資訊位數越多,可用的顔色就越多,顔色表現就越逼真,相應的資料量越大。例如,位深度為1的像素位圖隻有兩個可能的值(黑色和白色),是以又稱為二值位圖。位深度為8的圖像有28(即256)個可能的值。位深度為8的灰階模式圖像有256個可能的灰色值。
移動端開發中矢量圖的應用很少,對于區塊資訊,因為很少遇到這種需要無限縮放的場景。對于少數有縮放需要的場景,shape以及Bitmap類提供的(九點圖)功能已經可以滿足絕大多數場景。
每個像素使用的資訊位數越多,可用的顔色就越多,顔色表現就越逼真,相應的資料量越大,Android定義了這樣幾種圖檔格式。
4444已經被廢棄,因為顯示品質不好。
8888是Bitmap預設的顔色配置資訊,也是最占空間的一種配置。
565 如果不需要 alpha 通道,特别是資源本身為 jpg 格式的情況下,用這個格式比較理想
Skia是一個開源的二維圖形庫,提供各種常用的API,并可在多種軟硬體平台上運作。谷歌Chrome浏覽器、Chrome OS、安卓、火狐浏覽器、火狐作業系統以及其它許多産品都使用它作為圖形引擎。
已知Bitmap模式為ARGB_8888,假設這張圖最後加載成一個300 * 300 的Bitmap ,那麼 記憶體中的大小應該是?
Bitmap.CompressFormat.JPEG:表示以JPEG壓縮算法進行圖像壓縮,壓縮後的格式可以是“.jpg”或者“.jpeg”,是一種有損壓縮。
JPEG是最普遍在網際網路用來儲存和傳輸照片的格式。
Bitmap.CompressFormat.PNG:表示以PNG壓縮算法進行圖像壓縮,壓縮後的格式可以是“.png”,是一種無損壓縮。這意味着在生成檔案時,可能會忽略掉 品質。
和剛才的Bitmap.Config相比,這個内部類隻會在壓縮檔案等時被用到
Bitmap.CompressFormat.WEBP 2010年谷歌推遲的圖檔格式,專門用來在web中使用; 第一個版本的webp圖檔格式是有損的, 新版本中webp圖檔是無損的。
它的壓縮率是三者中最高的。
WebP
lossless images are 26% smaller in size compared to PNGs. WebP lossy
images are 25-34% smaller than comparable JPEG images at equivalent SSIM
quality index.
Lossless WebP supports transparency (also known as
alpha channel) at a cost of just 22% additional bytes. For cases when
lossy RGB compression is acceptable, lossy WebP also supports
transparency, typically providing 3× smaller file sizes compared to PNG.
讓我們首先了解一下Bitmap從檔案生成的流程。
首先說Bitmap是一個final類,是以不能被繼承。Bitmap隻有一個構造方法,且該構造方法是沒有任何通路權限修飾符修飾,也就是說該構造方法是friendly,但是谷歌稱Bitmap的構造方法是private(私有的),感覺有點不嚴謹。不管怎樣,一般情況下,我們不能通過構造方法直接建立一個Bitmap對象。從檔案建立Bitmap類就離不開BitmapFactory
BitmapFactory類提供了四類方法:decodeFile、decodeRe-source、decodeStream和decodeByteArray,分别用于支援從檔案系統、資源、輸入流以及位元組數組中加載出一個Bitmap對象,其中decodeFile和decodeResource又間接調用了decode-Stream方法,這四類方法最終是在Android的底層實作的,對應着BitmapFactory類的幾個native方法。
其實核心思想也很簡單,那就是采用BitmapFactory.Options來加載所需尺寸的圖檔。通過BitmapFactory.Options來縮放圖檔,主要是用到了它的inSampleSize參數,即采樣率。當inSampleSize為1時,采樣後的圖檔大小為圖檔的原始大小;當inSampleSize大于1時,比如為2,那麼采樣後的圖檔其寬/高均為原圖大小的1/2,而像素數為原圖的1/4,其占有的記憶體大小也為原圖的1/4。
擷取到占用空間盡可能小
縮放圖檔,主要是用到了它的inSampleSize參數,即采樣率。當inSampleSize為1時,采樣後的圖檔大小為圖檔的原始大小;當inSampleSize大于1時,比如為2,那麼采樣後的圖檔其寬/高均為原圖大小的1/2,而像素數為原圖的1/4,其占有的記憶體大小也為原圖的1/4。
(1)将BitmapFactory.Options的inJustDecodeBounds參數設為true并加載圖檔。 這一步并不會讀取檔案的像素區塊。隻會去從
(2)從BitmapFactory.Options中取出圖檔的原始寬高資訊,它們對應于outWidth和outHeight參數。
(3)根據采樣率的規則并結合目标View的所需大小計算出采樣率inSampleSize。
(4)将BitmapFactory.Options的inJustDecodeBounds參數設為false,然後重新加載圖檔。
先縮小後處理,如需要對圖檔做高斯模糊
(我們現在有個需求,要求将一張圖檔進行模糊,然後作為
ImageView 的 src 呈現給使用者,而我們的原始圖檔大小為
1080*1920,如果我們直接拿來模糊的話,一方面模糊的過程費時費力,另一方面生成的圖檔又占用記憶體,實際上在模糊運算過程中可能會存在輸入和輸出并存的情況,此時記憶體将會有一個短暫的峰值。
對于Bitmap而言,從檔案中讀取出來,工作還遠未結束。Bitmap的應用在很多時候都是用來建構一個BitmapDrawable,然後再去設定給别人做背景。
當然這隻是其中一種的gc觸發路徑。在别的很多情況下都有可能,但是recycler并不會觸發gc,或者說recycler方法并不能在性能上帶來提升。gc的事情還是去交給gc去做吧。