Bitmap.recycle引發(fā)的血案

從Bitmap.recycle說起

在Android中,Bitmap的存儲分為兩部分,一部分是Bitmap的數(shù)據(jù),一部分是Bitmap的引用。

在Android2.3時代,Bitmap的引用是放在堆中的,而Bitmap的數(shù)據(jù)部分是放在棧中的,需要用戶調(diào)用recycle方法手動進(jìn)行內(nèi)存回收,而在Android2.3之后,整個Bitmap,包括數(shù)據(jù)和引用,都放在了堆中,這樣,整個Bitmap的回收就全部交給GC了,這個recycle方法就再也不需要使用了。

然而……

現(xiàn)在的SDK中對recycle方法是這樣注釋的,如圖所示:

bitmap.png

可以發(fā)現(xiàn),系統(tǒng)建議你不要手動去調(diào)用,而是讓GC來進(jìn)行處理不再使用的Bitmap。我們可以認(rèn)為,即使在Android2.3之后的版本中去調(diào)用recycle,系統(tǒng)也是會強(qiáng)制回收內(nèi)存的,只是系統(tǒng)不建議這樣做而已。

鄙司代碼有些是從Android2.3出來的,因此很多地方還在使用Bitmap.recycle。通常情況下,這也沒什么問題,但是,今天遇到一個bug引發(fā)了Bitmap.recycle的血案。

起因

這個bug的起因是因為我們的一張圖片需要旋轉(zhuǎn),同時可以設(shè)置一個旋轉(zhuǎn)角度,老的代碼是這樣寫的:

ImageView imageView = (ImageView) findViewById(R.id.test);
Matrix matrix = new Matrix();
matrix.setRotate(0.013558723994643297f);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
Bitmap targetBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
if (!bitmap.isRecycled()) {
    bitmap.recycle();
}
imageView.setImageBitmap(targetBmp);

除了中間的0.013558723994643297f這串比較奇葩的數(shù)據(jù)(當(dāng)然,正常情況下都是20、30這樣正常的數(shù)),其它都是比較正常的代碼。

但實際上,只要一運行這段代碼,程序就會崩潰,錯誤原因如下所示:

E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: com.xys.preferencetest, PID: 30512
                  java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@1a50ff6b

這個問題一看就知道是由于Bitmap被調(diào)用recycle方法回收后,又調(diào)用了Bitmap的一些方法而導(dǎo)致的。可是,代碼中可以發(fā)現(xiàn)我們recycle的是bitmap而不是通過Bitmap.createBitmap重新生成的targetBmp,為什么會報這個exception呢?

注釋

按道理來說,bitmap與create出來的targetBmp應(yīng)該是兩個對象,當(dāng)旋轉(zhuǎn)角度正常的時候,確實也是這樣,但當(dāng)旋轉(zhuǎn)角度比較奇葩的時候,這兩個bitmap對象居然變成了同一個!而打開Bitmap.createBitmap的代碼,可以發(fā)現(xiàn)如下所示的注釋:

bitmap2.png

這里居然寫著:The new bitmap may be the same object as source, or a copy may have been made.

看來還是真有可能為同一個對象的!

猜測

經(jīng)過幾次嘗試,發(fā)現(xiàn)只有在角度很小很小的時候,才會出現(xiàn)這個情況,兩個bitmap是同一個對象,因此,我只能這樣猜測,當(dāng)角度過小時,系統(tǒng)認(rèn)為這是一張圖片,沒有發(fā)生變化,那么系統(tǒng)就直接引用同一個對象來進(jìn)行操作,避免內(nèi)存浪費。那么這個角度是怎么來的呢?繼續(xù)猜測,如圖所示:

bitmap3.png

當(dāng)圖像的旋轉(zhuǎn)角度小余兩個像素點之間的夾角時,圖像即使選擇也無法顯示,因此,系統(tǒng)完全可以認(rèn)為圖像沒有發(fā)生變化,因此,注釋中的情況,是不是有可能就是說的這種情況呢?

我還沒有來得及繼續(xù)驗證,希望大家可以一起討論下~有說的不對的還請指教。

然而……

然而,教訓(xùn)是,在不兼容Android2.3的情況下,別在使用recycle方法來管理Bitmap了,那是GC的事!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容