【问题标题】:BitmapFactory.decodeStream returning null when options are set设置选项时 BitmapFactory.decodeStream 返回 null
【发布时间】:2011-01-31 00:15:11
【问题描述】:

我遇到了BitmapFactory.decodeStream(inputStream) 的问题。当不带选项使用它时,它将返回一个图像。但是当我将它与.decodeStream(inputStream, null, options) 中的选项一起使用时,它永远不会返回位图。

我要做的是在实际加载位图之前对其进行下采样以节省内存。 我读过一些很好的指南,但没有使用.decodeStream

工作正常

URL url = new URL(sUrl);
HttpURLConnection connection  = (HttpURLConnection) url.openConnection();

InputStream is = connection.getInputStream();
Bitmap img = BitmapFactory.decodeStream(is, null, options);

不起作用

InputStream is = connection.getInputStream();
Bitmap img = BitmapFactory.decodeStream(is, null, options);

InputStream is = connection.getInputStream();

Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

BitmapFactory.decodeStream(is, null, options);

Boolean scaleByHeight = Math.abs(options.outHeight - TARGET_HEIGHT) >= Math.abs(options.outWidth - TARGET_WIDTH);

if (options.outHeight * options.outWidth * 2 >= 200*100*2){
    // Load, scaling to smallest power of 2 that'll get it <= desired dimensions
    double sampleSize = scaleByHeight
    ? options.outHeight / TARGET_HEIGHT
    : options.outWidth / TARGET_WIDTH;
    options.inSampleSize =
        (int)Math.pow(2d, Math.floor(
        Math.log(sampleSize)/Math.log(2d)));
}

// Do the actual decoding
options.inJustDecodeBounds = false;
Bitmap img = BitmapFactory.decodeStream(is, null, options);

【问题讨论】:

  • System.out.println("Samplesize: " ...) 语句的输出是什么?是否表明 options.inSampleSize 是可接受的值?
  • 是的,它每次都返回一个可接受的值。
  • 由于正在调试,删除了该语句。
  • 感谢您发布您的解决方案,但还有一件事要做。此问题仍会出现在“未解决的问题”列表中,因为您尚未将回复标记为“已接受”。您可以通过单击答案旁边的勾号图标来做到这一点。如果您觉得 Samuh 的答案有助于您找到解决方案,您可以接受它,或者您可以发布自己的答案并接受它。 (通常您会将您的解决方案放在您的答案中,但由于您已经通过编辑您的问题将其包含在内,您可以将它们引向该问题。)
  • 感谢您帮助新用户融入社区 :)

标签: java android image bitmap


【解决方案1】:

我认为问题在于“计算比例因子”逻辑,因为其余代码对我来说看起来是正确的(当然假设输入流不为空)。

如果您可以将此例程中的所有大小计算逻辑分解到一个方法中(称为 calculateScaleFactor() 或其他)并首先独立测试该方法,那就更好了。

类似:

// Get the stream 
InputStream is = mUrl.openStream();

// get the Image bounds
BitmapFactory.Options options=new BitmapFactory.Options(); 
options.inJustDecodeBounds = true;

bitmap = BitmapFactory.decodeStream(is,null,options);

//get actual width x height of the image and calculate the scale factor
options.inSampleSize = getScaleFactor(options.outWidth,options.outHeight,
                view.getWidth(),view.getHeight());

options.inJustDecodeBounds = false;
bitmap=BitmapFactory.decodeStream(mUrl.openStream(),null,options);

并独立测试 getScaleFactor(...)。

如果还没有完成,用 try..catch{} 块包围整个代码也将有所帮助。

【讨论】:

  • 非常感谢您的回答!我尝试设置一个最终的 int 值,例如“options.inSampleSize = 2”。但它会导致同样的问题。对于我尝试解码的每张图像,Logcat 都会读取“SkImageDecoder::Factory 返回 null”。在 try/catch 块中运行代码对我没有帮助,因为它不会抛出任何东西,对吧?但是,如果 BitmapFactory.decodeStream 无法创建 img,它会返回 null,而当我尝试使用 sampleSize 时它不能。
  • 这很奇怪。您可以尝试调整资源中捆绑的一些位图的大小吗?就像打开一个资源文件并尝试解码它一样。如果你能做到这一点,那么远程流可能存在一些问题,导致解码失败。
  • BitmapFactory.decodeResource(this.getResources(), R.drawable.icon, options) == null) 适用于重新采样。第一个带有 options.inJustDecodeBounds = true 的 BitmapFactory.decodeStream 可以正常工作并返回选项。但是以下带有 options.inJustDecodeBounds = false 的 BitmapFactory.decodeStream 每次都失败。
  • 恐怕这超出了我的能力范围......我很想知道这里可能出现什么问题,因为我使用的是类似的代码,它对我来说很好。
  • 好的。我已经解决了。问题在于http连接。当您从 HttpUrlConnection 提供的输入流中读取一次时,您无法再次读取它,并且必须重新连接才能执行第二个 decodeStream()。
【解决方案2】:

问题在于,一旦您使用 HttpUrlConnection 中的 InputStream 来获取图像元数据,您就无法回退并再次使用相同的 InputStream。

因此,您必须为图像的实际采样创建一个新的 InputStream。

  Options options = new BitmapFactory.Options();
  options.inJustDecodeBounds = true;

  BitmapFactory.decodeStream(is, null, options);

  Boolean scaleByHeight = Math.abs(options.outHeight - TARGET_HEIGHT) >= Math.abs(options.outWidth - TARGET_WIDTH);

  if(options.outHeight * options.outWidth * 2 >= 200*200*2){
         // Load, scaling to smallest power of 2 that'll get it <= desired dimensions
        double sampleSize = scaleByHeight
              ? options.outHeight / TARGET_HEIGHT
              : options.outWidth / TARGET_WIDTH;
        options.inSampleSize = 
              (int)Math.pow(2d, Math.floor(
              Math.log(sampleSize)/Math.log(2d)));
     }

        // Do the actual decoding
        options.inJustDecodeBounds = false;

        is.close();
        is = getHTTPConnectionInputStream(sUrl);
        Bitmap img = BitmapFactory.decodeStream(is, null, options);
        is.close();

【讨论】:

  • 这是否意味着图像必须下载两次?一次获取大小,一次获取像素数据?
  • @Robert,您可能应该解释这种特殊行为,以便其他用户对此有清楚的了解
  • 我想知道为什么我自己不能使用相同的输入流,感谢您的简短解释
  • 您不必重新创建它,只需重置它即可解决目的。看我的回答
  • 我不得不说Android的Bitmap类很烂。使用起来非常混乱和令人沮丧。
【解决方案3】:

尝试用 BufferedInputStream 包装 InputStream。

InputStream is = new BufferedInputStream(conn.getInputStream());
is.mark(is.available());
// Do the bound decoding
// inJustDecodeBounds =true
is.reset();  
// Do the actual decoding

【讨论】:

  • 它总是对你有用吗?出于某种原因,我在使用这种方法的一些非常具体的情况下得到空值。我在这里写了一篇关于它的帖子:stackoverflow.com/questions/17774442/…
  • 它有效,所以我支持它,但 is.available() 文档带有警告,它应该只用于检查流是否为空,而不是用于计算大小,因为这是不可靠的。跨度>
  • 投了反对票,但有问题的输入流连接是 HTTP 连接,reset() 不起作用....
【解决方案4】:

您可以将 InputStream 转换为字节数组,并使用 decodeByteArray()。例如,

public static Bitmap decodeSampledBitmapFromStream(InputStream inputStream, int reqWidth, int reqHeight) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    try {
        int n;
        byte[] buffer = new byte[1024];
        while ((n = inputStream.read(buffer)) > 0) {
            outputStream.write(buffer, 0, n);
        }
        return decodeSampledBitmapFromByteArray(outputStream.toByteArray(), reqWidth, reqHeight);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return null;
}

public static Bitmap decodeSampledBitmapFromByteArray(byte[] data, int reqWidth, int reqHeight) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeByteArray(data, 0, data.length, options);
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeByteArray(data, 0, data.length, options);
}

private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int
        reqHeight) {
    int width = options.outWidth;
    int height = options.outHeight;
    int inSampleSize = 1;
    if (width > reqWidth || height > reqHeight) {
        int halfWidth = width / 2;
        int halfHeight = height / 2;
        while (halfWidth / inSampleSize >= reqWidth && halfHeight / inSampleSize >= reqHeight) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-25
    • 2011-05-23
    • 1970-01-01
    • 2017-03-10
    • 2012-11-24
    • 1970-01-01
    相关资源
    最近更新 更多