忽略掉遙遠的Android2.2及之前版本,本文討論基於Android4.4,也適用於4.x版本。
項目開發中遇到一個內存溢出問題,抓內存數據分析,發現有的bitmap在java層生成,佔用的Dalvik虛擬機堆棧,這也符合我當前粗略的認知,恕本人知識淺陋。原來研究內存,一直認爲Android2.2之後的各個版本Android中bitmap都是通過JNI回調到Java層,最後是在Java層真正new出來的bitmap(這個在本文最後取源碼做了說明)。可是抓到的信息發現了一個Native stack上的Bitmap,如下圖所示。
然後查看資料和源碼發現,的確這個是在Native Stack上創建的,因爲它是調用BitmapFactory.decodeStream方法,創建出一個bitmap,decodeStream直接調用 JNI 的 nativeDecodeAsset() 來完成decode,這點不同於使用java層的createBitmap。所以這個是在Native層佔用的內存。
private static Bitmap readBitMap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
// 獲取資源圖片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
這裏代碼搬一下別人的代碼來說明,如何做就在Native層生成Bitmap了,如上代碼,Options需要設置兩個參數, inPurgeable 、inInputShareable爲true。其實看源碼分析,如果調用decodeStream方法,其實只要 inPurgeable
爲true就夠了,inInputShareable是默認爲true的,不過別的方法就需要雙雙爲ture,稍後源碼分析詳述此處。大家只要如上調用就可以讓bitmap在Native上了。具體性能會不會有影響,稍後我會寫幾個demo驗證一下。
如圖就是源碼中的調用,圖爲BitmapFactory這個源碼中decodeStream方法的具體實現,我們可以看到是調用的Native方法,可能有人會問了如果走了else呢,其實else裏面的方法跟進去就會發現,也是做了一些處理後,調用nativeDecodeAsset來生成的bitmap。
說到這裏我們必須說一個重要的事情,大家千萬別以爲去調用Native方法就一定是在Native層佔用的內存,例如Bitmap.java中的createBitmap也是調用的JNI方法,但其實又反過來回調的Java中的New來做的生成操作。具體的這個地方,我會專門寫一篇文章來講Bitmap和BitmapFactory的源碼,從源碼中徹底分析一下爲什麼就是在Native中生成的。