BitmapFactory.decodeStream คืนค่า null เมื่อตั้งค่าตัวเลือก


90

BitmapFactory.decodeStream(inputStream)ผมมีปัญหาเกี่ยวกับ เมื่อใช้งานโดยไม่มีตัวเลือกจะส่งคืนรูปภาพ แต่เมื่อฉันใช้กับตัวเลือกเช่นเดียวกับ.decodeStream(inputStream, null, options)มันจะไม่ส่งคืน Bitmaps

สิ่งที่ฉันกำลังพยายามทำคือการย่อขนาดบิตแมปก่อนที่ฉันจะโหลดจริงเพื่อบันทึกหน่วยความจำ ฉันได้อ่านคำแนะนำที่ดีบางอย่าง .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 เป็นค่าที่ยอมรับได้หรือไม่?
Steve Haley

ใช่จะส่งคืนค่าที่ยอมรับได้ทุกครั้ง
Robert Foss

ลบคำสั่งเนื่องจากถูกดีบัก
Robert Foss

1
ขอบคุณที่โพสต์วิธีแก้ปัญหาของคุณ แต่มีอีกเพียงสิ่งเดียวที่ต้องทำ คำถามนี้ยังคงปรากฏในรายการ "คำถามที่ยังไม่ได้แก้ไข" เนื่องจากคุณไม่ได้ทำเครื่องหมายคำตอบว่า "ยอมรับ" คุณสามารถทำได้โดยคลิกไอคอนเครื่องหมายถูกข้างคำตอบ คุณสามารถยอมรับคำตอบของ Samuh ได้หากคุณรู้สึกว่ามันช่วยให้คุณพบวิธีแก้ปัญหาหรือคุณสามารถโพสต์คำตอบของคุณเองและยอมรับได้ (โดยปกติคุณจะใส่คำตอบไว้ในคำตอบของคุณ แต่เนื่องจากคุณได้รวมสิ่งนั้นไว้แล้วโดยการแก้ไขคำถามของคุณคุณก็สามารถอ้างถึงคำถามนั้นได้)
Steve Haley

ขอบคุณที่ช่วยเหลือผู้ใช้ใหม่ในการรวมเข้ากับชุมชน :)
Robert Foss

คำตอบ:


114

ปัญหาคือเมื่อคุณใช้ InputStream จาก HttpUrlConnection เพื่อดึงข้อมูลเมตาของรูปภาพคุณจะไม่สามารถกรอกลับและใช้ 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

1
@ โรเบิร์ตคุณควรอธิบายพฤติกรรมเฉพาะนี้เพื่อให้ผู้ใช้รายอื่นได้รับความคิดที่ชัดเจนเกี่ยวกับเรื่องนี้
มูฮัมหมัดบาบาร์

1
ฉันสงสัยว่าทำไมมันไม่ทำงานกับอินพุตเดียวกันด้วยตัวเองขอบคุณสำหรับคำอธิบายสั้น ๆ
kabuto178

1
คุณไม่จำเป็นต้องสร้างมันขึ้นมาใหม่เพียงแค่รีเซ็ตมันก็จะช่วยแก้จุดประสงค์ได้ ดูคำตอบของฉัน
Shashank Tomar

5
ฉันต้องบอกว่าระดับ Bitmap ของ Android นั้นแย่มาก มันสับสนและน่าหงุดหงิดที่จะใช้
Neon Warge

30

ลองห่อ InputStream ด้วย BufferedInputStream

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/…
นักพัฒนาแอนดรอยด์

1
มันใช้งานได้ดังนั้นฉันจึงโหวตให้ แต่ is.available () doc มาพร้อมกับคำเตือนว่าควรใช้เพื่อตรวจสอบว่าสตรีมว่างหรือไม่เท่านั้นและไม่ใช่สำหรับการคำนวณขนาดเนื่องจากไม่น่าเชื่อถือ
Abhishek Chauhan

1
ลงคะแนน แต่การเชื่อมต่ออินพุตสตรีมที่เป็นปัญหาคือการเชื่อมต่อ HTTP และการรีเซ็ต () จะไม่ทำงาน ....
Johnny Wu

3

ฉันคิดว่าปัญหาเกิดจากตรรกะ "คำนวณมาตราส่วน - ตัวประกอบ" เพราะโค้ดส่วนที่เหลือดูถูกต้องสำหรับฉัน (สมมติว่าอินพุตสตรีมไม่เป็นโมฆะ)

มันจะดีกว่าถ้าคุณสามารถแยกตรรกะการคำนวณขนาดทั้งหมดจากรูทีนนี้ออกเป็นวิธีการ (เรียกว่าการคำนวณสเกลแฟกเตอร์ () หรืออะไรก็ได้) และทดสอบวิธีนั้นโดยอิสระก่อน

สิ่งที่ต้องการ:

// 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 return null' สำหรับทุกภาพที่ฉันพยายามถอดรหัส การรันโค้ดภายในบล็อก try / catch จะไม่ช่วยฉันเพราะมันไม่ได้ขว้างอะไรเลยใช่ไหม อย่างไรก็ตาม BitmapFactory.decodeStream จะคืนค่า null หากไม่สามารถสร้าง img ซึ่งไม่สามารถทำได้เมื่อฉันพยายามใช้ sampleSize
Robert Foss

มันแปลก ๆ. คุณลองปรับขนาดบิตแมปที่รวมอยู่ในทรัพยากรของคุณได้ไหม เช่นเดียวกับการเปิดไฟล์ทรัพยากรและพยายามถอดรหัส หากคุณสามารถทำได้อาจมีปัญหากับสตรีมระยะไกลซึ่งทำให้การถอดรหัสล้มเหลว
Samuh

BitmapFactory.decodeResource (this.getResources (), R.drawable.icon ตัวเลือก) == null) ทำงานได้ดีกับการสุ่มตัวอย่างซ้ำ BitmapFactory.decodeStream ตัวแรกที่มี options.inJustDecodeBounds = true ใช้งานได้และส่งคืนตัวเลือกได้ดี แต่ BitmapFactory.decodeStream ต่อไปนี้พร้อมกับ options.inJustDecodeBounds = false ล้มเหลวทุกครั้ง
Robert Foss

ฉันกลัวว่านี่จะเกินกว่าฉัน ... ฉันสนใจที่จะรู้ว่ามีอะไรผิดพลาดที่นี่เพราะฉันใช้รหัสที่คล้ายกันและมันก็ใช้ได้ดีสำหรับฉัน
Samuh

4
ตกลง. ฉันได้แก้ไขแล้ว ปัญหาอยู่ที่การเชื่อมต่อ http เมื่อคุณอ่านจากอินพุตสตรีมที่ให้มาโดย HttpUrlConnection หนึ่งครั้งคุณจะไม่สามารถอ่านได้อีกและต้องเชื่อมต่อใหม่เพื่อทำการถอดรหัสสตรีมครั้งที่สอง ()
Robert Foss

2

คุณสามารถแปลง 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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.