1. 程式人生 > >BitmapFactory.decodeStream方法及如何將Raw中的圖片載入為Bitmap

BitmapFactory.decodeStream方法及如何將Raw中的圖片載入為Bitmap

結論

將輸入流傳遞給 BitmapFactory.decodeStream(in) 方法,建立完成 Bitmap 之後,開發者一定要主動去關閉這個輸入流。否則,對輸入流執行 reset() 方法,則可以重新獲取輸入流中的所有資料,並且創建出一張新的圖片。

從res-raw目錄下獲取一張Bitmap圖片(工具類)

    public static @Nullable
    Bitmap loadBitmapFromRawResource(@NonNull Context context, @RawRes int id) {
        InputStream inputStream = null;
        try {
            inputStream = context.getResources().openRawResource(id);
            Bitmap rawBitmap = BitmapFactory.decodeStream(inputStream);
            return rawBitmap;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
            }
        }
        return null;
    }

下面是測試用例及詳細異常


測試用例說明

1、從raw目錄下獲取一個輸入流
2、使用這個輸入流建立一張Bitmap圖片,並且丟棄之
3、根據 System.currentTimeMillis() % 2 是否為0,決定是否關閉這個輸入流
4、reset上面的輸入流
5、使用上面reset過的輸入流再建立一張Bitmap圖片
6、返回第二次建立的Bitmap圖片,並關閉輸入流 

測試用例(異常程式碼)

    public static @Nullable
    Bitmap loadBitmapFromRawResource(@NonNull Context context, @RawRes int id) {
        InputStream inputStream = null;
        try {
            inputStream = context.getResources().openRawResource(id);
            //第一次從流中建立圖片
            Bitmap rawBitmap = BitmapFactory.decodeStream(inputStream);
            /*********************************************************/
            /*模擬 decodeStream 之後,開發者是否記得主動關閉輸入流的兩種情況*/
            if (System.currentTimeMillis() % 2 == 0){
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                } catch (IOException e) {
                }
            }
            /*********************************************************/
            //1、如果,僅將 inputStream 直接丟給 BitmapFactory.decodeStream 方法,下面執行 inputStream.reset() 可以重新獲取流中所有資料,並創建出正常的 Bitmap 圖片。
            //2、如果,在 BitmapFactory.decodeStream 讀取完成之後,呼叫 inputStream.close() 方法。下面執行 reset() 方法時,會報
            // "java.lang.NullPointerException: asset at android.content.res.AssetManager.seekAsset(Native Method) at android.content.res.AssetManager.-wrap4(Unknown Source:0)" 異常
            //結論:BitmapFactory.decodeStream 處理完流之後,輸入流一定記得關。各位,一定記得關流啊!
            try {
                inputStream.reset();
            }catch (Exception e){
                e.printStackTrace();
            }
            //第二次從流中建立圖片
            //如果開發者忘記關流,第二次可以建立成功。
            //如果開發都把已經關閉的流交給 decodeStream 來解析,應用將直接閃退。try-catch也沒有用。異常資訊是:
            //"/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 25381 (RxIoScheduler-5)"
            Bitmap testRawBitmap = BitmapFactory.decodeStream(inputStream);
            TestToastUtil.toastAll("" + testRawBitmap);
            return testRawBitmap;
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
            }
        }
        return null;
    }

對已經close的InputStream執行reset方法,產生的異常

10-19 17:08:52.542 22783-22961/cn.dxy.android.aspirin W/System.err: java.lang.NullPointerException: asset
        at android.content.res.AssetManager.seekAsset(Native Method)
10-19 17:08:52.543 22783-22961/cn.dxy.android.aspirin W/System.err:     at android.content.res.AssetManager.-wrap4(Unknown Source:0)
        at android.content.res.AssetManager$AssetInputStream.reset(AssetManager.java:642)
        at cn.dxy.aspirin.utils.LoadBitmapFromViewUtil.loadBitmapFromRawResource(LoadBitmapFromViewUtil.java:43)
10-19 17:08:52.544 22783-22961/cn.dxy.android.aspirin W/System.err:     at cn.dxy.android.aspirin.dailyhealth.DailyHealthTruthPresenter.lambda$shareWechatMoment$1$DailyHealthTruthPresenter(DailyHealthTruthPresenter.java:149)
10-19 17:08:52.545 22783-22961/cn.dxy.android.aspirin W/System.err:     at cn.dxy.android.aspirin.dailyhealth.DailyHealthTruthPresenter$$Lambda$1.call(Unknown Source:8)
        at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69)
        at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:77)
10-19 17:08:52.546 22783-22961/cn.dxy.android.aspirin W/System.err:     at rx.internal.util.ScalarSynchronousObservable$ScalarAsyncProducer.call(ScalarSynchronousObservable.java:200)
        at rx.internal.util.ScalarSynchronousObservable$2$1.call(ScalarSynchronousObservable.java:114)
        at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230)
10-19 17:08:52.547 22783-22961/cn.dxy.android.aspirin W/System.err:     at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
10-19 17:08:52.548 22783-22961/cn.dxy.android.aspirin W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
10-19 17:08:52.549 22783-22961/cn.dxy.android.aspirin W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)

參考網址

輸入流InputStream的reset()和mark()方法注意事項
探索Bitmap使用姿勢