當前位置:
首頁 > 最新 > 閱讀Android源碼BitmapFactory

閱讀Android源碼BitmapFactory

涼涼

 三生三世桃花開

叢瑞 

00:00/05:35

引言

一首《涼涼送給大家》,最近的工作猶如這個歌曲;還是開始今天的主題吧,BitmapFactory 想必不陌生了,我在項目中也用到很多,但是從來沒有真的仔細的去看看這個東西,哈哈,所以今天想帶大家簡單的看看,順便呢,找出一些解決因為載入bitmap導致的OOM(Out Of Memory)的問題;

OOM是什麼?

在使用C或C++語言時,我們可操作的內存空間就是整個設備的物理內存,程序員需要自己聲明內存空間,也需要自己在恰當的時機釋放掉內存,一旦出錯就會造成內存泄漏。而Java語言為了解決這個問題,在操作系統之上創造了一個Java虛擬機(JVM),讓Java語言編譯後的位元組碼運行在此虛擬機之上。啟動一個Java應用,會首先啟動JVM,JVM 會向操作系統申請所需內存,然後把內存分成為棧內存和堆內存。堆內存用以存放對象實例,並可被Java回收機制回收,一旦剩餘堆內存空間不夠申請新對象時就會產生OutOfMemoryError異常。

Android內存管理

Android的Dalvik虛擬機(DVM)是參考JVM做出來的,所以大同小異。最主要的兩個區別是:一.DVM 基於寄存器,而JVM基於棧來進行局部變數的操作,當然在性能上DVM會更快;二.在DVM上運行的是被進一步處理的JAVA位元組碼,後綴為.dex,.dex 是把Java應用中所有的.class文件合併而成,縮減了包的體積。Android中的 DVM 如 JVM 一樣對每個應用可使用的最大內存空間做了限制,每台設備出廠之前廠家就對單個DVM實例可使用的最大內存進行了限定。

由於上面的限制和內存的管理,當我們使用Android的Bitmap的時候會出現載入圖片過大導致的程序的內存泄漏;推薦大家檢測內存泄漏的軟體。用過這個,感覺不錯;


LeakCanary 內存泄漏檢測工具

造成的原因


下面我們來看看Bitmap類的構造方法:

/**

* Private constructor that must received an already allocated native bitmap

* int (pointer).

*/

// called from JNI

Bitmap(longnativeBitmap, byte[] buffer, intwidth, intheight, intdensity,

booleanisMutable, booleanrequestPremultiplied,

byte[] ninePatchChunk,NinePatch.InsetStruct ninePatchInsets) {

if(nativeBitmap ==) {

throw newRuntimeException("internal error: native bitmap is 0");

}

mWidth = width;

mHeight = height;

mIsMutable = isMutable;

mRequestPremultiplied = requestPremultiplied;

mBuffer = buffer;

mNinePatchChunk = ninePatchChunk;

mNinePatchInsets = ninePatchInsets;

if(density >=) {

mDensity = density;

}

mNativePtr = nativeBitmap;

mFinalizer =newBitmapFinalizer(nativeBitmap);

intnativeAllocationByteCount = (buffer ==null? getByteCount() :);

mFinalizer.setNativeAllocationByteCount(nativeAllocationByteCount);

}

在bitmap類中,並沒有靜態的獲得對象的方法,所以我們只能把希望寄托在工廠類了.

下面看看BitmapFactory

BitmapFactory中,有提供一些靜態的方法去獲得一個bitmap對象,主要方法如下:

全部方法如上圖。現在我們看看這幾個主要用到的方法;

a) 從文件載入

public static Bitmap decodeFile(String pathName, Options opts)

public static Bitmap decodeFile(String pathName)

b)從資源中載入

public static Bitmap decodeResourceStream(Resources res,

TypedValue value,

InputStream is, Rect pad, Options opts)

public static Bitmap decodeResource(Resources res, int id,

Options opts)

c)從二進位數組中載入文件

public static Bitmap decodeByteArray(byte[] data, int offset,

int length, Options opts)

public static Bitmap decodeByteArray(byte[] data, int offset,

int length)

d)從流中載入

public static Bitmap decodeStream(InputStream is, Rect outPadding,

Options opts)

private static Bitmap decodeStreamInternal(InputStream is,

Rect outPadding, Options opts)

public static Bitmap decodeStream(InputStream is)

e)從文件描述符載入

public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

public static Bitmap decodeFileDescriptor(FileDescriptor fd)

上述五對方法,都會有相對應一組中參數少的一個調用參數多的一個方法,最後轉到調用幾個native的方法:

1.

private static native Bitmap nativeDecodeStream(InputStream is,

byte[] storage,Rect padding, Options opts);

2.

private static native Bitmap nativeDecodeFileDescriptor(

FileDescriptor fd,Rect padding, Options opts);

3.

private static native Bitmap nativeDecodeAsset(long nativeAsset,

Rect padding, Options opts);

4.

private static native Bitmap nativeDecodeByteArray(byte[] data,

int offset,int length, Options opts);

5.

private static native boolean nativeIsSeekable(FileDescriptor fd);

下面以分析;

首先是一個靜態代碼塊。先看看(看註解哦)

public static classOptions {

/**

* Create a default Options object, which if left unchanged will give

* the same result from the decoder as if null were passed.

*/

publicOptions() {

inDither =false;

inScaled =true;

inPremultiplied =true;

}

.....code....

/**

* 如果設置為true,解碼器將返回null(沒有點陣圖),

* 但是輸出…仍然會設置欄位,允許調用者查詢點陣圖,

* 而不必為其像素分配內存。

*/

public booleaninJustDecodeBounds;

/**

* 如果設置為值> 1,請解碼器對原始圖像進行子樣本化,

* 返回較小的圖像以保存內存。

* 樣本大小是每個維對應解碼點陣圖中單個像素的像素個數。

* 例如,inSampleSize == 4返回一個圖像,

* 它的寬度/高度是原來的1/4,像素的數量是1/16。

* 任何值

* 任何其他值都將四捨五入到2次冪。

*

*/

public intinSampleSize;

/*

* 如果這是非空的,

* 解碼器將嘗試解碼到這個內部配置。

* 如果它是空的,或者請求不能被滿足,

* 解碼器將嘗試根據系統的屏幕深度選擇最佳匹配配置,

* 以及原始圖像的特徵,例如如果它有每個像素的alpha值(需要配置它也可以)。

*

*/

publicBitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;

.....code....

/*

* 點陣圖的結果寬度。

*/

public intoutWidth;

/*

* 點陣圖的結果高度。

*/

public intoutHeight;

///...code.....

}

下面我們以decodeFile為例 :

/**

* pathName:地址名稱

* Options :可以為null, 控制下採樣的選項,以及是否應該完全解碼圖像,或者返回大小。

*/

public staticBitmap decodeFile(String pathName,Options opts) {

Bitmap bm =null;

InputStream stream =null;

try{

stream =newFileInputStream(pathName);

bm = decodeStream(stream, null,opts);

}catch(Exception e) {

/* do nothing.

If the exception happened on open, bm will be null.

*/

Log.e("BitmapFactory","Unable to decode stream: "+ e);

}finally{

if(stream !=null) {

try{

stream.close();

}catch(IOException e) {

// do nothing here

}

}

}

returnbm;

}

如上方法中調用了decodeStream,那我們就轉到這個地方看看;

/*

* is: 將原始數據解碼為點陣圖的輸入流。

*

* outPadding:如果不為空,則為點陣圖返回填充矩形(如果存在),

* 否則將填充設置為[-1,-1,-1,-1]。

* 如果沒有返回點陣圖為null,那麼填充是不變的。

* opts:可以為null, 控制下採樣的選項,以及是否應該完全解碼圖像,或者返回大小。

*/

public staticBitmap decodeStream(InputStream is,Rect outPadding,

Options opts) {

// we don"t throw in this case, thus allowing the caller

// to only check

// the cache, and not force the image to be decoded.

if(is ==null) {

return null;

}

Bitmap bm =null;

Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS,"decodeBitmap");

try{

if(isinstanceofAssetManager.AssetInputStream) {

final longasset = ((AssetManager.AssetInputStream) is)

.getNativeAsset();

bm = nativeDecodeAsset(asset,outPadding,opts);

}else{

bm = decodeStreamInternal(is,outPadding,opts);

}

if(bm ==null&& opts !=null&& opts.inBitmap !=null) {

throw newIllegalArgumentException("Problem decoding

into existing bitmap");

}

setDensityFromOptions(bm,opts);

}finally{

Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);

}

returnbm;

}

在處理的時候涉及到nativeDecodeAsset和decodeStreamInternal方法,我們看看這是啥?

如果is 是Asset中的,擇使用第一個方法;通過C層直接處理;

如果is不屬於Asset;擇使用第二個方法返回bm (bitmap)

private staticBitmap decodeStreamInternal(InputStream is,

Rect outPadding,Options opts) {

// ASSERT(is != null);

byte[] tempStorage =null;

if(opts !=null) tempStorage = opts.inTempStorage;

if(tempStorage ==null) tempStorage =new byte[DECODE_BUFFER_SIZE];

returnnativeDecodeStream(is,tempStorage,outPadding,opts);

}

最終調用了nativeDecodeStream方法;看來都是地層來實現的;看來是否要去看看C層的東西? 搞起吧,不搞起都不知道咋回事了,干~

在android-6.0.1_r72/frameworks/base/core/jni/android/graphics/

BitmapFactory.cpp

文件中找到如上方法:

staticjobject nativeDecodeStream(JNIEnv* env,jobject clazz,

jobject is,jbyteArray storage,

jobject padding,jobject options) {

//創建一個bitmap對象

jobject bitmap = NULL;

//創建一個輸入流適配器,(SkAutoTUnref)自解引用

SkAutoTDelete stream(CreateJavaInputStreamAdaptor(env,

is,storage));

if(stream.get()) {

SkAutoTDelete bufferedStream(

SkFrontBufferedStream::Create(stream.detach(),

BYTES_TO_BUFFER));

SkASSERT(bufferedStream.get() != NULL);

//圖片解碼

bitmap = doDecode(env,bufferedStream,padding,options);

}

//返回圖片對象,載入失敗的時候返回空

returnbitmap;

}

總結:

相對比較乾貨比較少,其實知道了方法還得去運用,本次還沒寫運用,我後面補充吧。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 七秒的空間 的精彩文章:

TAG:七秒的空間 |