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使用姿势

猜你喜欢

转载自blog.csdn.net/ybf326/article/details/83186206
今日推荐