【问题标题】:BitmapFactory.decodeStream returns null, when downloading a image form webBitmapFactory.decodeStream 在下载图像表单时返回 null
【发布时间】:2012-11-24 09:58:57
【问题描述】:

我正在尝试使用Google Example Page 从 URL 下载图像。当我在 BitmapFactory.decodeStream 方法中使用 InputStream 时,我有read,我不能使用两次。我正在尝试这样做,但它不起作用,因为它在解码的图像中返回 null,我不知道我能做什么。

这是我的代码:

这部分在 AsyncTask 类的 doInBackground 方法中

Bitmap bitmapImage;
URL imageUrl = null;
try {
imageUrl = new URL(url[0]);

HttpGet httpRequest = null;
httpRequest = new HttpGet(imageUrl.toURI());

HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = (HttpResponse) httpclient.execute(httpRequest);

HttpEntity entity = response.getEntity();
BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
InputStream instream = bufHttpEntity.getContent();

    bitmapImage = CommonMethods.decodeSampledBitmapFromResource(instream, thumb_width, thumb_width);

instream.close();
return bitmapImage;

 } catch (URISyntaxException e) {
    e.printStackTrace();
    return null;
 } catch (MalformedURLException e) {
    e.printStackTrace();
    return null;
 } catch (IOException e) {
    e.printStackTrace();
    return null;
 }


 public static Bitmap decodeSampledBitmapFromResource(InputStream instream,
        int reqWidth, int reqHeight) throws IOException {

    //Copy instream for decode twice 
ByteArrayOutputStream out = new ByteArrayOutputStream();
    copy(instream,out);
    ByteArrayInputStream instream2 = new ByteArrayInputStream(out.toByteArray());

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(instream, null, options);
    instream2.close();

    options.inJustDecodeBounds = false;

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    return BitmapFactory.decodeStream(instream, null, options);
 }

 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) {
         if (width > height) {
         inSampleSize = Math.round((float) height / (float) reqHeight);
     } else {
             inSampleSize = Math.round((float) width / (float) reqWidth);
     }
     }

     return inSampleSize;
}

//Copy instream method
public static void copy(InputStream input, OutputStream output) throws IOException{

     byte[] buffer = new byte[Constants.IO_BUFFER_SIZE];

 int n = 0;

 while (-1 != (n = input.read(buffer))) {

     output.write(buffer, 0, n);
 }
 }

【问题讨论】:

标签: android bitmap bitmapfactory


【解决方案1】:

BitmapFactory.decodeStream 返回 null 因为输入流被使用了两次,我没有尝试过你的代码,但它接缝没问题,或者我错了。 无论如何,我有一个更好的解决方案。只需使用 BufferedInputStream 包装 inputStream,然后在第二次读取之前,先调用“reset”。注意普通的 inputStreams 不支持“reset”,你可以调用它但什么都不会发生。 我的代码:

    public static Bitmap decodeSampledBitmapFromStream(InputStream inputStream,
                                                   int reqWidth, int reqHeight)
                             throws IOException {
    if (!widthHeightCheck(reqWidth, reqHeight)) 
        return BitmapFactory.decodeStream(inputStream);
    // First decode with inJustDecodeBounds=true to check dimensions
    if (!(inputStream instanceof BufferedInputStream)) {
        inputStream = new BufferedInputStream(inputStream);
    }
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    Rect rect = new Rect(-1, -1, -1, -1);
    BitmapFactory.decodeStream(inputStream, rect, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    inputStream.reset();
    return BitmapFactory.decodeStream(inputStream, rect, options);
}

【讨论】:

【解决方案2】:

我认为您可以通过将您从 httpEntity 获得的流包装在自定义 WrappedStream 中来实现这一点。这个 WrappedStream 将在读取原始流时提供第二个输入流。 (这是通过 PipedStream 完成的)

使用此代码获取图像大小后:

options.inJustDecodeBounds = true;
WrappedStream wrappedStream = new WrappedStream(instream);
BitmapFactory.decodeStream(wrappedStream, null, options);

你可以打电话

InputStream reReadStream = wrappedStream.getReReadStream();
options.inJustDecodeBounds = false;
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// Decode bitmap with inSampleSize set
return BitmapFactory.decodeStream(reReadStream, null, options);

最后,这里是 WrappedStream 的实现(它只是将所有调用委托给被包装的 inputStream,并将所有读取(或跳过)的字节写入 pipedOutputStream)

import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

/** Simple class wrapping an InputStream and feeding a secondary InputStream
 *  to re-read the data that was originally available in the inputStream.
**/

public class WrappedStream extends InputStream {

private InputStream urlStream;
private PipedOutputStream pipedStream;

public WrappedStream(InputStream urlStream) {
    this.urlStream = urlStream;
    this.pipedStream = new PipedOutputStream();
}

/**
 * return a fresh InputStream to re-read the data
 */
public InputStream getReReadStream() throws IOException {
    return new PipedInputStream(pipedStream);
}
@Override
public int available() throws IOException {
    return urlStream.available();
}

@Override
public void close() throws IOException {
    urlStream.close();
}

@Override
public void mark(int readlimit) {
    urlStream.mark(readlimit);
}

@Override
public boolean markSupported() {
    return urlStream.markSupported();
}

@Override
public int read() throws IOException {
    int b = urlStream.read();
    pipedStream.write(b);
    return b;
}

@Override
public int read(byte[] buffer) throws IOException {
    int l = urlStream.read(buffer);
    pipedStream.write(buffer);
    return l;
}

@Override
public int read(byte[] buffer, int offset, int length) throws IOException {
    int l = urlStream.read(buffer, offset, length);
    pipedStream.write(buffer, offset, length);
    return l;
}

@Override
public void reset() throws IOException {
    urlStream.reset();
}

@Override
//bytes skipped must available on the re-read stream so we read and write them.
public long skip(long byteCount) throws IOException {
    long bytesToSkip = byteCount;
    long skippedBytes = 0;
//ugly trick required to not loosing bytes if we ever skip more than Integer.MAX_VALUE bytes
    while(bytesToSkip>Integer.MAX_VALUE){
        _skip(Integer.MAX_VALUE);
        bytesToSkip -=Integer.MAX_VALUE;
        skippedBytes +=Integer.MAX_VALUE;
    }
    byte[] b = new byte[(int)bytesToSkip];
    skippedBytes += read(b);
    return skippedBytes;
}

private int _skip(int byteCount) throws IOException {
    byte[] b = new byte[(int)byteCount];
    return read(b);
}
}

请注意,我没有测试此代码。这只是为了给你一些关于如何解决你的问题的想法。

另外一点:即使这段代码从不创建一个巨大的位图,整个流将保存在内存中,直到生成缩放的位图。

【讨论】:

    【解决方案3】:

    找到适合你的代码

    final HttpEntity entity = response.getEntity();
                if (entity != null) {
                    InputStream inputStream = null;
                    try {
                        inputStream = entity.getContent();
                        BitmapFactory.Options options = new BitmapFactory.Options();
                        //options.inSampleSize = 2;
                        final Bitmap bitmap = BitmapFactory
                                .decodeStream(inputStream, null, options);
                        return bitmap;
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        if (inputStream != null) {
                            inputStream.close();
                        }
                        entity.consumeContent();
                    }
                }
    

    请替换正确的变量,如果您想缩放图像,您可以在获取位图后对其进行缩放。

    【讨论】:

    • 我已经测试了其他解决方案但不起作用。我会尝试以其他形式做到这一点,但现在,我更喜欢这个解决方案。解码后缩放图像,并在解码后的位图中使用弱引用,如下所示:developer.android.com/training/displaying-bitmaps/….
    【解决方案4】:

    这是从服务器下载位图的方法,您可以使用更少的代码来满足您的要求

    Bitmap downloadBitmap(String url)
            {
                Bitmap image = null;
                InputStream in = null;
                try
                    {
                        in = new java.net.URL(url).openStream();
                        BitmapFactory.Options opts = new BitmapFactory.Options();
                        opts.inSampleSize = 2;
                        image = BitmapFactory.decodeStream(new FlushedInputStream(in),null,opts);
                        in.close();
                    }
                catch (MalformedURLException e)
                    {
                        e.printStackTrace();
                    }
                catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                return image;
            }
    

    在上面的代码中我们使用 opts.inSampleSize = 2; 这意味着位图将减少到其原始大小的一半以避免内存异常,如果我们正在加载大量图像,我们必须这样做

    其中使用了一些其他类

            static class FlushedInputStream extends FilterInputStream
            {
                public FlushedInputStream( InputStream inputStream )
                    {
                        super(inputStream);
                    }
    
                @Override
                public long skip(long n) throws IOException
                    {
                        long totalBytesSkipped = 0L;
                        while (totalBytesSkipped < n)
                            {
                                long bytesSkipped = in.skip(n - totalBytesSkipped);
                                if (bytesSkipped == 0L)
                                    {
                                        int byte1 = read();
                                        if (byte1 < 0)
                                            {
                                                break; // we reached EOF
                                            }
                                        else
                                            {
                                                bytesSkipped = 1; // we read one byte
                                            }
                                    }
                                totalBytesSkipped += bytesSkipped;
                            }
                        return totalBytesSkipped;
                    }
            }
    

    【讨论】:

      【解决方案5】:

      请使用以下代码下载图像并将其显示到图像视图中。

      public class image extends Activity {
          /** Called when the activity is first created. */
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.main);
      
              Bitmap bitmap = DownloadImage("http://www.gophoto.it/view.php?i=http://1.bp.blogspot.com/-2LTvCCufBKc/T3L3KgcTj2I/AAAAAAAABbQ/Ki60e1LU9sE/s1600/Sachin%2BTendulkar.png");
              ImageView img = (ImageView) findViewById(R.id.img);
              img.setImageBitmap(bitmap);
          }
      
          private InputStream OpenHttpConnection(String urlString) throws IOException {
              InputStream in = null;
              int response = -1;
      
              URL url = new URL(urlString);
              URLConnection conn = url.openConnection();
      
              if (!(conn instanceof HttpURLConnection))
                  throw new IOException("Not an HTTP connection");
      
              try {
                  HttpURLConnection httpConn = (HttpURLConnection) conn;
                  httpConn.setAllowUserInteraction(false);
                  httpConn.setInstanceFollowRedirects(true);
                  httpConn.setRequestMethod("GET");
                  httpConn.connect();
                  response = httpConn.getResponseCode();
                  if (response == HttpURLConnection.HTTP_OK) {
                      in = httpConn.getInputStream();
                  }
              } catch (Exception ex) {
                  throw new IOException("Error connecting");
              }
              return in;
          }
      
          private Bitmap DownloadImage(String URL) {
              Bitmap bitmap = null;
              InputStream in = null;
              try {
                  in = OpenHttpConnection(URL);
                  bitmap = BitmapFactory.decodeStream(in);
                  in.close();
              } catch (IOException e1) {
                  // TODO Auto-generated catch block
                  e1.printStackTrace();
              }
              return bitmap;
          }
      }
      

      【讨论】:

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