BitmapFactory.decodeStream, seçenekler ayarlandığında boş döndürüyor


90

İle ilgili sorunlar yaşıyorum BitmapFactory.decodeStream(inputStream). Seçenek olmadan kullanıldığında, bir görüntü döndürür. Ama onu seçeneklerle kullandığımda .decodeStream(inputStream, null, options)asla Bitmapler döndürmüyor.

Yapmaya çalıştığım şey, bellekten tasarruf etmek için aslında yüklemeden önce bir Bitmap'i altörneklemek. Bazı iyi rehberler okudum ama hiçbiri kullanmıyor .decodeStream.

SADECE GÜZEL İŞLER

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

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

ÇALIŞMIYOR

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:" ...) ifadenizin çıktısı nedir? Options.inSampleSize'nin kabul edilebilir bir değer olduğunu belirtmek mi?
Steve Haley

Evet, her seferinde kabul edilebilir bir değer döndürür.
Robert Foss

Hata ayıklanması nedeniyle ifade kaldırıldı.
Robert Foss

1
Çözümünüzü yayınladığınız için teşekkürler, ancak yapılacak bir şey daha var. Bu soru, bir yanıtı "kabul edildi" olarak işaretlemediğiniz için "çözülmemiş sorular" listelerinde görünmeye devam ediyor. Bir cevabın yanındaki onay işaretine tıklayarak bunu yapabilirsiniz. Çözümü bulmanıza yardımcı olduğunu düşünüyorsanız Samuh'un cevabını kabul edebilirsiniz veya kendi cevabınızı gönderip kabul edebilirsiniz. (Normalde çözümünüzü cevabınıza koyardınız, ancak bunu zaten sorunuzu düzenleyerek dahil ettiğiniz için, onları soruya yönlendirebilirsiniz.)
Steve Haley

Yeni bir kullanıcının topluluğa entegre olmasına yardımcı olduğunuz için teşekkürler :)
Robert Foss

Yanıtlar:


114

Sorun, görüntü meta verilerini almak için bir HttpUrlConnection'dan bir InputStream kullandıktan sonra, aynı InputStream'i geri alamaz ve tekrar kullanamazsınız.

Bu nedenle, görüntünün gerçek örneklemesi için yeni bir InputStream oluşturmanız gerekir.

  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
Bu, görüntünün iki kez indirilmesi gerektiği anlamına mı geliyor? Boyutu almak için bir kez ve piksel verilerini almak için bir kez?
user123321

1
Diğer kullanıcıların bu konuda net bir fikir elde yüzden muhtemelen bu davranışını açıklamalıdır @Robert
Muhammed Babar

1
Neden aynı girdi akışıyla çalışmayacağını merak ediyordum, kısa açıklama için teşekkürler
kabuto178

1
onu yeniden yaratmak zorunda değilsiniz, sadece onu sıfırlamak amacı çözecektir. Cevabımı görün
Shashank Tomar

5
Android'in Bitmap sınıfının berbat olduğunu söylemeliyim. Kullanımı çok kafa karıştırıcı ve sinir bozucu.
Neon Warge

30

InputStream'i BufferedInputStream ile sarmayı deneyin.

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

2
senin için hep işe yaradı mı? Bazı nedenlerden dolayı, bu yöntemi kullanarak bazı çok özel durumlarda boş alıyorum. burada bununla ilgili bir gönderi yazdım: stackoverflow.com/questions/17774442/…
android geliştiricisi

1
işe yaradı, bu yüzden ona oy verdim, ancak is.available () doc, bu güvenilmez olduğu için boyutu hesaplamak için değil, yalnızca akışın boş olup olmadığını kontrol etmek için kullanılması gerektiği konusunda uyarıda bulunur.
Abhishek Chauhan

1
aşağı oy verildi, ancak söz konusu giriş akışı bağlantısı bir HTTP bağlantısı ve sıfırlama () çalışmıyor ....
Johnny Wu

3

Sanırım problem "hesapla-ölçekleme faktörü" mantığıyla ilgili çünkü kodun geri kalanı bana doğru görünüyor (tabii ki girdi akışının boş olmadığını varsayarak).

Bu rutinden tüm boyut hesaplama mantığını bir yönteme ayırıp (hesaplamaScaleFactor () veya başka bir şey olarak adlandırın) ve bu yöntemi önce bağımsız olarak test etmeniz daha iyi olur.

Gibi bir şey:

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

ve getScaleFactor (...) 'ı bağımsız olarak test edin.

Zaten yapılmadıysa, kodun tamamını try..catch {} bloğu ile çevrelemek de yardımcı olacaktır.


Cevap için çok teşekkürler! 'Options.inSampleSize = 2' gibi son bir int değeri ayarlamayı denedim. Ama aynı sorunlara yol açar. Logcat, kodunu çözmeye çalıştığım her görüntü için 'SkImageDecoder :: Factory null döndürdü' okur. Kodu bir dene / yakala bloğu içinde çalıştırmak hiçbir şey atmadığından bana yardımcı olmaz, değil mi? Ancak BitmapFactory.decodeStream, bir sampleSize kullanmaya çalıştığımda bunu yapamayan bir img oluşturamazsa null döndürür.
Robert Foss

Bu tuhaf. Kaynağınızda bulunan bazı Bitmap'leri yeniden boyutlandırmayı deneyebilir misiniz? Bir kaynak dosyasını açın ve kodunu çözmeye çalışın. Bunu yapabiliyorsanız, uzak akışta kod çözmenin başarısız olmasına neden olan bir sorun olabilir.
Samuh

BitmapFactory.decodeResource (this.getResources (), R.drawable.icon, options) == null) yeniden örnekleme ile iyi çalışıyor. Options.inJustDecodeBounds = true değerine sahip ilk BitmapFactory.decodeStream, iyi çalışır ve seçenekleri döndürür. Ancak options.inJustDecodeBounds = false ile aşağıdaki BitmapFactory.decodeStream her seferinde başarısız olur.
Robert Foss

Korkarım bu beni aşıyor ... Burada neyin yanlış gidebileceğini bilmek isterim çünkü benzer kod kullanıyorum ve benim için gayet iyi çalışıyor.
Samuh

4
Tamam. Ben çözdüm. Sorun http bağlantısında yatıyor. HttpUrlConnection tarafından sağlanan giriş akışından bir kez okuduğunuzda, ondan tekrar okuyamazsınız ve ikinci decodeStream () işlemini yapmak için yeniden bağlanmanız gerekir.
Robert Foss

2

InputStream'i bir bayt dizisine dönüştürebilir ve decodeByteArray () yöntemini kullanabilirsiniz. Örneğin,

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.