從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方法是這樣注釋的,如圖所示:

可以發(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)如下所示的注釋:

這里居然寫著: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ù)猜測,如圖所示:

當(dāng)圖像的旋轉(zhuǎn)角度小余兩個像素點之間的夾角時,圖像即使選擇也無法顯示,因此,系統(tǒng)完全可以認(rèn)為圖像沒有發(fā)生變化,因此,注釋中的情況,是不是有可能就是說的這種情況呢?
我還沒有來得及繼續(xù)驗證,希望大家可以一起討論下~有說的不對的還請指教。
然而……
然而,教訓(xùn)是,在不兼容Android2.3的情況下,別在使用recycle方法來管理Bitmap了,那是GC的事!