2015-08-20 16 views
107

Görüntülerin sunucudan cihaza gönderildiği, resim yoğun bir sosyal uygulama yapıyorum. Cihazda daha küçük ekran çözünürlükleri olduğunda, istenen ekran boyutlarına uyacak şekilde cihazdaki bitmapleri yeniden boyutlandırmam gerekir.Android'de bitmap'leri yeniden boyutlandırmak için en verimli bellek yolu?

Sorun, createScaledBitmap kullanımının, bir sürü küçük resim görüntüsünü yeniden boyutlandırdıktan sonra çok fazla bellek yetersizliği hatasına neden olmamı sağlamasıdır.

Android'de bitmap'leri yeniden boyutlandırmak için en etkili bellek yolu hangisidir?

+7

Sunucunuzun doğru boyutunu göndermemesi, böylece müşterinin RAM'ini ve bant genişliğinizi koruyabilir !? – James

+2

Sadece sunucu kaynağına sahip olsaydım, onunla birlikte bir hesaplama bileşeni vardı ve her durumda, henüz görmediği en boy oranları için görüntülerin tam boyutlarını tahmin edebilirdi. Eğer kaynak içeriği üçüncü taraf bir CDN'den yüklüyorsanız (benim gibi) işe yaramazsa :( –

cevap

154

Bu cevap aşağı ölçekli bitmap sürümünü yüklemek için inSampleSize nasıl kullanılacağı açıklanır Loading large bitmaps Efficiently den özetlenmiştir. Pre-scaling bitmaps Özellikle

bunları birleştirmek nasıl çeşitli yöntemlerin ayrıntılarını açıklıyor ve hangi verimli çoğu bellek vardır.

farklı bellek özelliklerine sahip Android'de bir bit eşlem yeniden boyutlandırmak için üç baskın yolu vardır:

createScaledBitmap API

Bu API mevcut bitmap'te almak ve bir YENİ bit eşlemi yaratacak seçtiğiniz boyutlar.

Artı tarafta, tam olarak aradığınız görüntü boyutunu elde edebilirsiniz (nasıl göründüğüne bakılmaksızın). Ancak olumsuz, , bu API çalışmak için mevcut bitmap gerektirir. Yeni, daha küçük bir sürüm oluşturmadan önce görüntünün yüklenmesi, kodunun çözülmesi ve bir bitmap oluşturulması gerekir. Bu tam boyutlarınızı almak açısından daha fazla, ama ek bellek yükü açısından korkunç. Bunun gibi, bu bilinçli belleği inSampleSize flag

BitmapFactory.Options bir özellik Çözümün sırasında görüntüyü yeniden boyutlandırmak olacak inSampleSize olarak not etmiştir

, önlemek olma eğilimi çoğu uygulama geliştirici için bir anlaşma kırıcı tür-taşımaktadır Geçici bir bitmap'e çözümleme ihtiyacı. Burada kullanılan bu tamsayı değeri, 1/x küçültülmüş boyutta bir görüntü yükler. Örneğin, inSampleSize değerini 2 olarak ayarlamak, boyutunun yarısı kadar olan bir görüntü döndürür ve 4 olarak ayarladığınızda, boyutun 1/4'üne sahip bir görüntü döndürür. Temel olarak resim boyutları, her zaman kaynak boyutunuzdan biraz daha küçük bir güç olacaktır.

Bellek açısından bakıldığında, inSampleSize kullanımı gerçekten hızlı bir işlemdir. Etkin olarak, görüntünüzün her X. pikseli yalnızca sonuçta elde ettiğiniz bitmap'e çözer.

  • Size kesin kararlar vermez: gerçi inSampleSize ile iki ana sorunlar var. Sadece bitmap'in boyutunu yalnızca 2 gücüyle azaltır.

  • En iyi kalitede yeniden boyutlandırma'u üretmez. Yeniden boyutlandırılan filtrelerin çoğu, piksel bloklarını okuyarak iyi görünen görüntüler oluşturur ve ardından söz konusu yeniden boyutlandırılan pikseli üretmek için onları ağırlığa sokar.inSampleSize, yalnızca birkaç pikselin hepsini okuyarak bunu engeller. Sonuç oldukça performanslıdır ve düşük hafızadır, ancak kalite zarar görür. Yalnızca bazı pow2 boyutuna göre görüntüyü küçülen uğraşıyoruz ve filtreleme sorunu değilse

, o zaman inSampleSize daha fazla bellek verimli (veya performans verimli) yöntemi bulamıyor. Eğer iki gücüne eşit olmayan bir boyuta bir görüntü ölçeklendirme gerekiyorsa

inScaled, inDensity, inTargetDensity flags

, o zaman BitmapOptions ait inScaled, inDensity ve inTargetDensity bayrakları gerekir. inScaled bayrağı ayarlandığında, sistem inTargetDensity değerlerini inDensity değerlerine bölerek ölçekleme değerinizi bitmap'inize uygulayacaktır.

mBitmapOptions.inScaled = true; 
mBitmapOptions.inDensity = srcWidth; 
mBitmapOptions.inTargetDensity = dstWidth; 

// will load & resize the image to be 1/inSampleSize dimensions 
mCurrentBitmap = BitmapFactory.decodeResources(getResources(), 
     mImageIDs, mBitmapOptions); 

resim boyutunda yeniden ve ayrıca ek hesaplamalar dikkate alınmıştır çünkü boyutlandırma adımı sırasında Sonuçta daha iyi bakacağız, yani buna bir 'yeniden boyutlandırma filtresi' uygulanacak bu yöntemi kullanarak . Ancak uyarıda bulunun: Ekstra filtre adımı olan , ekstra işlem süresi alır ve büyük resimler için hızlı bir şekilde ekleyebilir, sonuçta yavaş yeniden boyutlandırmalar ve filtrenin kendisinde ekstra bellek ayırmalarına neden olabilir.

Bu tekniği, ek filtreleme ek yükünden dolayı, istediğiniz boyuttan önemli ölçüde daha büyük olan bir görüntüye uygulamak genellikle iyi bir fikir değildir.

bir bellek ve performans açısından bakıldığında Sihirli Kombinasyon

, en iyi sonuçlar için bu seçenekleri birleştirebilirsiniz.

inSampleSize ilk görüntünün uygulanacaktır (inSampleSize, inScaled, inDensity ve inTargetDensity bayrakları ayarlama) onu alma hedef boyutundan daha güç-iki BÜYÜK yanında yer almaktadır. Ardından, görüntüyü temizlemek için bir filtre işlemi uygulayarak sonucu istediğiniz boyutlara ölçeklendirmek için inDensity & kullanılır.

inSampleSize adımı, elde edilen Yoğunluk tabanlı adımın yeniden boyutlandırma filtresini uygulamasının gerekeceği piksel sayısını azaltacağından, bu ikisinin birleştirilmesi çok daha hızlı bir işlemdir.

mBitmapOptions.inScaled = true; 
mBitmapOptions.inSampleSize = 4; 
mBitmapOptions.inDensity = srcWidth; 
mBitmapOptions.inTargetDensity = dstWidth * mBitmapOptions.inSampleSize; 

// will load & resize the image to be 1/inSampleSize dimensions 
mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions); 

belirli boyutlarda ve bazı güzel filtrelemeye bir görüntü sığdırmak gerek ediyorsanız, o zaman bu teknik doğru boyutta almak için en iyi köprü olmakla hızlı, düşük bellek ayak izi yapılır operasyon. senin bit eşlem yeniden boyutlandırmak için resmin tamamını deşifre olmadan görüntü boyutunu alma

Alma resim boyutları

, gelen boyutlarını bilmek gerekir. Görüntünün boyutlarını elde etmenize yardımcı olması için inJustDecodeBounds bayrağını kullanabilirsiniz.

// Decode just the boundaries 
mBitmapOptions.inJustDecodeBounds = true; 
BitmapFactory.decodeFile(fileName, mBitmapOptions); 
srcWidth = mBitmapOptions.outWidth; 
srcHeight = mBitmapOptions.outHeight; 


//now go resize the image to the size you want 
Önce boyutunu çözmek için bu bayrağı kullanabilirler ve sonra hedef çözünürlüğe ölçeklendirme uygun değerlerini hesaplayabilir

.

+1

dstWidth'in ne olduğunu bize söyleyebilirseniz harika olurdu? – k0sh

+0

@ k0sh dstWIdth, ImageView'ın genişliğidir. Nereye yani 'hedef genişlik 'ya da kısa – tyczj

+0

@tyczj için dstWidth için nereye gidiyor, biliyorum, ne olduğunu biliyorum, ama bunu bilmiyor olabilir ve aslında bu soruyu cevaplayan Colt beri, belki o açıklayabilir Bu yüzden insanlar kafamı karıştırmazlar ... – k0sh

11

Bu yanıtın güzel (ve doğru) olduğu kadar, aynı zamanda oldukça karmaşıktır. Tekerleği yeniden icat etmek yerine, Glide, Picasso, UIL, Ion gibi kütüphaneleri veya bu karmaşık ve hata eğilimli mantığı sizin için uygulayan başka herhangi bir sayıyı düşünün.

Colt'un kendisi bile, Pre-scaling Bitmaps Performance Patterns Video numaralı telefondan Glide ve Picasso'ya bakmanızı önerir.

Kitaplıkları kullanarak, Colt'in yanıtında belirtilen her bit verimi alabilirsiniz, ancak Android'in her sürümünde tutarlı olarak çalışan çok daha basit API'lerle.

İlgili konular