【问题标题】:BitmapFactory.decodeStream from Assets returns null on Android 7来自 Assets 的 BitmapFactory.decodeStream 在 Android 7 上返回 null
【发布时间】:2017-01-11 23:12:40
【问题描述】:

如何在 Android 7 中从 Asset 目录中解码位图?

我的应用在 Marshmallow 之前的 Android 版本上运行良好。在 Android 7 中,它无法从 Asset 目录加载图像。

我的代码:

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);

}

因此(仅限 Android 7)BitmapFactory.decodeStream 为空。它可以在较旧的 Android API 上正常工作。

在调试模式下,我看到以下消息:

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

谁能告诉我原因以及如何更正编码?

编辑:同时我发现,使用 inJustDecodeBounds=true 删除第一个 BitmapFactory.decodeStream 会导致成功的 BitmapFactory.decodeStream 之后使用 inJustDecodeBounds=false。不知道原因,也不知道如何替换位图大小的测量。

【问题讨论】:

  • 我的猜测是您的问题是特定于您的资产的。我刚刚在运行 Android 7.0 的 Nexus 9 上测试了从资产加载图像的 one of my book samples。它似乎工作正常。

标签: android bitmapfactory android-assets android-7.0-nougat


【解决方案1】:

如果这对任何人都有帮助,我在更新以前用于调整图像大小的旧代码时遇到了类似的问题。 我的问题是我从图像文件中读取数据的堆栈。我使用了已弃用的IOUtils.toByteArray(Reader)。我切换到直接从 URI 转换为字节数组,现在它运行良好。有关该新方法的示例,请参见下面resizeImage() 的前两行(其余代码允许我调整图像大小。)

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;
}

【讨论】:

    【解决方案2】:

    我认为我们在同一条船上。我的团队和你一样在这个问题上停留了一段时间。

    BitmapFactory.cpp 中似乎有问题 (https://android.googlesource.com/platform/frameworks/base.git/+/master/core/jni/android/graphics/BitmapFactory.cpp) 在 Android 7.0 中添加了一些代码并导致问题发生。

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

    我发现BitmapFactory.decodeStream 方法在我们设置inJustDecodeBounds=false 后并没有创建位图,但是当我尝试在没有绑定解码的情况下创建位图时。其作品!问题在于 BitmapOptions,当我们再次调用 BitmapFactory.decodeStream 时,InputStream 没有更新。

    所以我在再次解码之前重置了那个 InputStream

    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;
    }
    

    看起来我们每次重用之前都必须重置 InputStream。

    【讨论】:

    • 这成功了。只需重置 InputStream。感谢您的帮助。
    • 我试过这个技巧,但在调用休息时我不断收到“不支持标记/重置”IOException。但我使用 FileInputStream,因为我尝试从 SD 卡加载位图。
    • 除了 FileInputStream 之外还有其他方法可以从存储中打开文件吗?
    • 谢谢,它帮助了我。 Android 7,文件是从资源中使用的(png 图像)。添加inputStream.reset() 在以前的版本中使用是安全的,所以如果您重新使用相同的输入流,请添加它。
    • Android 7.1.2 无法正常工作。为了使它工作,我必须为 close() 更改 reset(),然后在下一个 decodeStream() 之前创建新流
    【解决方案3】:

    我怀疑 android 默认安全配置不允许您使用协议“http://”(无加密)下载文件。

    尝试使用“https://”协议查找图像或文件。看看这是否会导致您的文件加载。否则,设置一个允许“http”的网络安全配置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-21
      • 1970-01-01
      • 2015-02-25
      • 2011-05-23
      • 2011-01-31
      相关资源
      最近更新 更多