结论
将输入流传递给 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使用姿势