Android 3.0以后烈炭,Bitmap的內(nèi)存默認(rèn)在Java堆上申請(qǐng)榜掌,而優(yōu)于虛擬機(jī)的限制优妙,Java堆大小是受限的。
申請(qǐng)超過(guò)堆最大內(nèi)存憎账,會(huì)引發(fā)OOM套硼;重復(fù)在堆上申請(qǐng)內(nèi)存,會(huì)引發(fā)頻繁的GC胞皱,GC會(huì)帶來(lái)額外的系統(tǒng)開銷邪意,一些情況下的GC會(huì)鎖定非GC線程從而引發(fā)UI卡頓。
關(guān)于Bitmap的內(nèi)存位置朴恳,邏輯在BitmapFactory的jni實(shí)現(xiàn)中抄罕,具體代碼在frameworks/base/core/jni/graphics/BitmapFactory.cpp,doDecode()函數(shù)于颖。
下面以4.4源碼分析:
這里的邏輯由于可以直接傳入kDecodeBounds_Mode類型的mode,以及isPurgeable嚷兔,變得相對(duì)復(fù)雜:
- isPurgeable == true:這種情況就是在Ashmem上申請(qǐng)Bitmap內(nèi)存的模式了
- 傳入mode == kDecodeBounds_Mode:這種情況實(shí)際上并不會(huì)真正解碼出Bitmap森渐,而只是給出Bitmap的信息
對(duì)應(yīng)的Java參數(shù)是BitmapFactory.Options的
根據(jù)是否decodeMode是否為kDecodeBounds_Mode,給SkImageDecoder設(shè)置不同的Allocator冒晰。
-
true同衣,不設(shè)置Allocator,此時(shí)SkImageDecoder的Allocator為NULL
這里可以分為兩方面解釋:
a壶运、對(duì)于傳入mode是kDecodeBounds_Mode的情況耐齐,本身并不會(huì)解碼出Bitmap,所以不需要Allocator;
b埠况、對(duì)于isPurgeable == true耸携,內(nèi)存將在AshMemory上申請(qǐng)
對(duì)于內(nèi)存的分析,僅針對(duì)isPurgeable==true的情況:- 由于decodeMode==kDecodeBounds_Mode的情況辕翰,decoder的decode并沒(méi)有真正alloc夺衍,而是提前返回了true
-
在獲取最終輸出的outputBitmap的SkPixelRef時(shí),purgeable的解碼操作才被處理
Paste_Image.png
SkPixelRef的類型是SkImageRef_ashmem
Paste_Image.png在SkImageRef的prepareBitmap函數(shù)中喜命,真正處理的解碼
Paste_Image.png傳入的Allocator是AshmemAllocator
Paste_Image.png由此產(chǎn)生的Bitmap內(nèi)存分布在Ashmem上面
-
false沟沙,此處分為幾種情況
-
需要對(duì)Bitmap進(jìn)行scale,并且javaBitmap != NULL壁榕,使用了ScaleCheckingAllocator
Paste_Image.png
當(dāng)需要解碼的Bitmap矛紫,經(jīng)過(guò)scale后,尺寸大于了inBitmap牌里,在解碼時(shí)會(huì)返回false含衔。否則,將申請(qǐng)一塊nativeHeap用于存放臨時(shí)的bitmap二庵。
而后贪染,將解碼出來(lái)的decodingBitmap,通過(guò)canvas畫在輸出的outputBitmap上面去催享。
Paste_Image.png-
不需要scale杭隙,javaBitmap != NULL
javaBitmap就是復(fù)用的inBitmap,不為NULL時(shí)因妙,這里使用了RecyclingPixelAllocator
Paste_Image.png
對(duì)應(yīng)的allocPixelRef其實(shí)并沒(méi)有真正的alloc內(nèi)存痰憎,而是將javaBitmap的SkPixelRef取出交給新的Bitmap使用
-
不需要scale,javaBitmap == NULL
此時(shí)Allocator是JavaPixelAllocator攀涵,其allocPixelRef就是從Java虛擬機(jī)中申請(qǐng)內(nèi)存铣耘。
Paste_Image.png
Paste_Image.png
-
總結(jié)一下:BitmapFactory根據(jù)isPurgeable、inBitmap以故、scale的屬性蜗细,使用了以下類型的Allocator:
- JavaPixelAllocator,在Java堆分配Pixels內(nèi)存怒详,默認(rèn)情況都使用這個(gè)
- RecyclingPixerAllocator炉媒,對(duì)于有inBitmap復(fù)用的情況,使用這個(gè)Allocator昆烁,本質(zhì)是將inBitmap的PixelRef賦值給解碼的Bitmap
- AshmemAllocator吊骤,對(duì)于設(shè)置了isPurgeable的情況,使用這個(gè)Allocator静尼,本質(zhì)是在Ashmem上分配Pixels內(nèi)存白粉。
- ScaleCheckingAllocator传泊,這是針對(duì)設(shè)置了scale,并且設(shè)置了inBitmap的情況鸭巴,目的是先做校驗(yàn)眷细,校驗(yàn)通過(guò)則申請(qǐng)臨時(shí)的native堆內(nèi)存存放臨時(shí)Bitmap,再將臨時(shí)Bitmap繪制在最后的outBitmap上面去奕扣。