设置选项后,BitmapFactory.decodeStream返回null


90

我有的问题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);

1
您的System.out.println(“ Samplesize:” ...)语句的输出是什么?是否表明options.inSampleSize是一个可接受的值?
史蒂夫·哈利

是的,它每次都返回一个可接受的值。
罗伯特·佛斯

由于正在调试,因此删除了该语句。
罗伯特·佛斯

1
感谢您发布您的解决方案,但还有另一件事要做。该问题仍出现在“未解决的问题”列表中,因为您尚未将回答标记为“已接受”。您可以通过单击答案旁边的对勾图标来实现。如果您认为Samuh的答案可以帮助您找到解决方案,则可以接受,也可以发布自己的答案并接受。(通常,您会在答案中添加解决方案,但是由于您已经通过编辑问题将其包括在内,因此您可以将他们引荐给该问题。)
Steve Haley 2010年

感谢您帮助新用户融入社区:)
Robert Foss 2010年

Answers:


114

问题在于,一旦使用了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();

17
这是否意味着图像必须下载两次?一次获得尺寸,一次获得像素数据?
user123321 2012年

1
@Robert您可能应该解释这种特殊的行为,以便其他用户对此有一个清晰的了解
Muhammad Babar 2013年

1
我想知道为什么我自己不能在相同的输入流中使用它,感谢您的简短解释
kabuto178 2014年

1
您不必重新创建它,只需重置它就可以解决目的。看到我的答案
Shashank Tomar 2014年

5
我不得不说Android的Bitmap类很烂。使用起来如此令人困惑和沮丧。
霓虹灯Warge'9

30

尝试使用BufferedInputStream包装InputStream。

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

2
它对您一直有效吗?由于某些原因,在使用此方法的某些非常特殊的情况下,我会得到空值。:我写了一个关于它的帖子在这里 stackoverflow.com/questions/17774442/...
Android开发者

1
它起作用了,所以我赞成它,但是is.available()doc带有警告,它仅应用于检查流是否为空,而不是用于计算大小,因为这是不可靠的。
Abhishek Chauhan 2014年

1
否决,但是有问题的输入流连接是HTTP连接,并且reset()无法使用....
Johnny Wu

3

我认为问题出在“计算比例因子”逻辑上,因为其余代码对我来说似乎是正确的(当然,假设inputstream不为null)。

最好将这个例程中的所有大小计算逻辑都分解为一个方法(称为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时,它将无法返回。
罗伯特·佛斯

这很奇怪。您可以尝试调整资源中捆绑的某些位图的大小吗?就像打开资源文件并尝试对其进行解码一样。如果可以这样做,则可能是远程流存在一些问题,导致解码失败。
Samuh 2010年

BitmapFactory.decodeResource(this.getResources(),R.drawable.icon,options)== null)可以很好地进行重新采样。第一个具有options.inJustDecodeBounds = true的BitmapFactory.decodeStream可以正常工作并返回选项。但是以下带有options.inJustDecodeBounds = false的BitmapFactory.decodeStream每次都会失败。
罗伯特·佛斯

恐怕这超出了我的范围...我想知道这里可能出什么问题,因为我正在使用类似的代码,它对我来说也很好。
Samuh 2010年

4
好。我已经解决了 问题出在http连接。从HttpUrlConnection提供的输入流中读取一次后,您将无法再次读取它,而必须重新连接以执行第二个解码流()。
罗伯特·佛斯

2

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

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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.