BitmapFactory.decodeStream from assets returns null on Android 7

How to decode bitmaps from the Asset directory in Android 7?

My app runs on versions of Android prior to Marshmallow. With Android 7, you cannot download images from the Asset directory.

My code is:

private Bitmap getImage(String imagename) { // Log.dd(logger, "AsyncImageLoader: " + ORDNER_IMAGES + imagename); AssetManager asset = context.getAssets(); InputStream is = null; try { is = asset.open(ORDNER_IMAGES + imagename); } catch (IOException e) { // Log.de(logger, "image konnte nicht gelesen werden: " + ORDNER_IMAGES + imagename); return null; } // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, PW, PH); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; // Lesen des Bitmaps in der optimierten Groesse return BitmapFactory.decodeStream(is, null, options); } 

As a result (Android 7 only), BitmapFactory.decodeStream is null. It works correctly with older Android APIs.

In debug mode, I see the following message:

09-04 10: 10: 50.384 6274-6610 / myapp D / skia: --- SkAndroidCodec :: NewFromStream returned null

Can someone tell me the reason and how to fix the encoding?

Edit: In the meantime, I found that removing the first BitmapFactory.decodeStream with inJustDecodeBounds = true results in a successful BitmapFactory.decodeStream subsequently with inJustDecodeBounds = false. You don’t know the reason and don’t know how to replace the bitmap size measurement.

+5
source share
2 answers

I think we are in the same boat. My team was stuck in this problem for a while, like you.

The problem seems to be in BitmapFactory.cpp ( https://android.googlesource.com/platform/frameworks/base.git/+/master/core/jni/android/graphics/BitmapFactory.cpp ). In Android 7.0, some code was added and a problem arose.

 // Create the codec. NinePatchPeeker peeker; std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(), &peeker)); if (!codec.get()) { return nullObjectReturn("SkAndroidCodec::NewFromStream returned null"); } 

And I found out that the BitmapFactory.decodeStream method BitmapFactory.decodeStream not create a bitmap after we set inJustDecodeBounds=false , but when I try to create a bitmap without bound decoding. It is working! The problem is that BitmapOptions is that the InputStream is not updated when we call BitmapFactory.decodeStream again.

So, I reset that InputStream before decoding again

 private Bitmap getBitmapFromAssets(Context context, String fileName, int width, int height) { AssetManager asset = context.getAssets(); InputStream is; try { is = asset.open(fileName); } catch (IOException e) { return null; } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, options); try { is.reset(); } catch (IOException e) { return null; } options.inSampleSize = calculateInSampleSize(options, width, height); options.inJustDecodeBounds = false; return BitmapFactory.decodeStream(is, null, options); } public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; } 

It looks like we should reset InputStream every time before reusing it.

+8
source

In case this helps someone, I ran into a similar problem updating the old code that previously worked for resizing images. My problem was higher on the stack where I was reading data from an image file. I used IOUtils.toByteArray(Reader) , which is deprecated. I switched to converting to a byte array directly from the URI, and now it works well. See the first two lines of resizeImage() below for an example of this new method (the rest of the code allows me to resize the image.)

 public static Bitmap resizeImage(Uri imageUri, int targetWidth, int targetHeight) { // Convert the image to a byte array java.net.URI tempUri = new URI(uri.toString()); byte[] imageData = IOUtils.toByteArray(tempUri); // First decode with inJustDecodeBounds=true to check dimensions BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; Bitmap reducedBitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options); Bitmap resizedBitmap = Bitmap.createScaledBitmap(reducedBitmap, targetWidth, targetHeight, false); return resizedBitmap; } public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a // power of 2 and keeps both height and width larger // than the requested height and width. while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; } 
0
source

All Articles