BitmapFactory.decodeStream trả về null khi các tùy chọn được đặt


90

Tôi đang gặp vấn đề với BitmapFactory.decodeStream(inputStream). Khi sử dụng nó mà không có tùy chọn, nó sẽ trả về một hình ảnh. Nhưng khi tôi sử dụng nó với các tùy chọn như trong .decodeStream(inputStream, null, options)nó không bao giờ trả về Bitmap.

Những gì tôi đang cố gắng làm là giảm mẫu một Bitmap trước khi tôi thực sự tải nó để tiết kiệm bộ nhớ. Tôi đã đọc một số hướng dẫn tốt, nhưng không sử dụng .decodeStream.

CÔNG TRÌNH JUST FINE

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

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

KHÔNG LÀM VIỆC

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
Kết quả đầu ra từ câu lệnh System.out.println ("Kích thước mẫu:" ...) của bạn là gì? Có phải chỉ ra rằng options.inSampleSize là một giá trị có thể chấp nhận được không?
Steve Haley

Có, nó trả về một giá trị chấp nhận được mọi lúc.
Robert Foss,

Đã xóa câu lệnh do nó đang được gỡ lỗi.
Robert Foss,

1
Cảm ơn bạn đã đăng giải pháp của mình, nhưng chỉ còn một việc nữa phải làm. Câu hỏi này vẫn xuất hiện trong danh sách "câu hỏi chưa được giải quyết" vì bạn chưa đánh dấu câu trả lời là "được chấp nhận". Bạn có thể làm điều đó bằng cách nhấp vào biểu tượng dấu tích bên cạnh câu trả lời. Bạn có thể chấp nhận câu trả lời của Samuh nếu bạn cảm thấy nó giúp bạn tìm ra giải pháp hoặc bạn có thể đăng câu trả lời của riêng mình và chấp nhận nó. (Thông thường bạn muốn đưa giải pháp của bạn trong câu trả lời của bạn, nhưng kể từ khi bạn đã đưa rằng bằng cách chỉnh sửa câu hỏi của bạn, bạn chỉ có thể giới thiệu họ đến câu hỏi.)
Steve Haley

Cảm ơn bạn đã giúp người dùng mới hòa nhập vào cộng đồng :)
Robert Foss

Câu trả lời:


114

Vấn đề là khi bạn đã sử dụng InputStream từ HttpUrlConnection để tìm nạp siêu dữ liệu hình ảnh, bạn không thể tua lại và sử dụng lại cùng một InputStream.

Do đó, bạn phải tạo một Dòng đầu vào mới để lấy mẫu hình ảnh thực tế.

  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
Điều này có nghĩa là hình ảnh phải tải xuống hai lần? Một lần để lấy kích thước và một lần để lấy dữ liệu pixel?
user123321 23/09/12

1
@Robert có lẽ bạn nên giải thích hành vi đặc biệt này để những người dùng khác có được một ý tưởng rõ ràng về điều đó
Muhammad Babar

1
Tôi đã tự hỏi tại sao nó không hoạt động với cùng một dòng đầu vào, cảm ơn vì lời giải thích ngắn gọn
kabuto178

1
bạn không cần phải tạo lại nó, việc gửi lại nó sẽ giải quyết được mục đích. Xem câu trả lời của tôi
Shashank Tomar

5
Tôi phải nói rằng lớp Bitmap của Android thật tệ. Nó rất khó hiểu và bực bội khi sử dụng.
Neon Warge

30

Hãy thử bọc InputStream bằng BufferedInputStream.

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

2
nó đã luôn luôn làm việc cho bạn? vì một số lý do, tôi nhận được null trong một số trường hợp rất cụ thể bằng cách sử dụng phương pháp này. tôi đã viết một bài về nó ở đây: stackoverflow.com/questions/17774442/…
nhà phát triển android vào

1
nó hoạt động vì vậy tôi đã ủng hộ nó nhưng doc is.available () đi kèm với cảnh báo rằng nó chỉ nên được sử dụng để kiểm tra xem luồng có trống hay không và không để tính kích thước vì điều này không đáng tin cậy.
Abhishek Chauhan

1
được bỏ phiếu thấp, nhưng kết nối luồng đầu vào được đề cập là kết nối HTTP và thiết lập lại () sẽ không hoạt động ....
Johnny Wu

3

Tôi nghĩ rằng vấn đề là với logic "tính toán-quy mô-yếu tố" vì phần còn lại của mã trông đúng với tôi (giả sử tất nhiên rằng dòng đầu vào không phải là null).

Sẽ tốt hơn nếu bạn có thể phân tích tất cả logic tính toán kích thước từ quy trình này thành một phương thức (gọi nó là allowScaleFactor () hoặc bất cứ thứ gì) và kiểm tra phương pháp đó một cách độc lập trước.

Cái gì đó như:

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

và kiểm tra getScaleFactor (...) một cách độc lập.

Nó cũng sẽ giúp bao quanh toàn bộ mã bằng khối try..catch {}, nếu bạn chưa thực hiện xong.


Cảm ơn rất nhiều cho câu trả lời! Tôi đã thử đặt một giá trị int cuối cùng như 'options.inSampleSize = 2'. Nhưng nó dẫn đến những vấn đề tương tự. Logcat đọc 'SkImageDecoder :: Factory trả về null', cho mọi hình ảnh mà tôi cố gắng giải mã. Chạy mã bên trong khối try / catch sẽ không giúp tôi vì nó không ném bất cứ thứ gì, phải không? Tuy nhiên BitmapFactory.decodeStream trả về null nếu nó không thể tạo img, điều này không thể xảy ra khi tôi cố gắng sử dụng sampleSize.
Robert Foss,

Điều này thật kỳ lạ. Bạn có thể thử thay đổi kích thước một số Bitmap có trong tài nguyên của mình không? Giống như mở một tệp tài nguyên và cố gắng giải mã nó. Nếu bạn có thể làm điều đó, có thể có một số vấn đề với luồng từ xa khiến quá trình giải mã không thành công.
Samuh

BitmapFactory.decodeResource (this.getResources (), R.drawable.icon, options) == null) hoạt động tốt với việc lấy mẫu lại. BitmapFactory.decodeStream đầu tiên với options.inJustDecodeBounds = true hoạt động và trả về các tùy chọn tốt. Nhưng BitmapFactory.decodeStream sau đây với options.inJustDecodeBounds = false lần nào cũng bị lỗi.
Robert Foss,

Tôi sợ điều này nằm ngoài phạm vi của tôi ... Tôi muốn biết điều gì có thể xảy ra ở đây vì tôi đang sử dụng mã tương tự và nó hoạt động tốt đối với tôi.
Samuh

4
Đồng ý. Tôi đã giải quyết nó. Vấn đề nằm ở kết nối http. Khi bạn đã đọc từ luồng đầu vào được cung cấp bởi HttpUrlConnection một lần, bạn không thể đọc lại từ luồng đó và phải kết nối lại để thực hiện lần giải mã thứ hai.
Robert Foss,

2

Bạn có thể chuyển đổi InputStream thành một mảng byte và sử dụng decodeByteArray (). Ví dụ,

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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.