用BitmapFactory.decodeStream()方法計算圖片采樣率的問題

在學(xué)習(xí)圖片加載時圖片很大的時候直接加載到內(nèi)存或則直接原圖繪制到 ImageView中會導(dǎo)致OOM問題溶耘;

解決思路是利用BitmapFactory的系列方法中有個帶有 BitmapFactory.Options 參數(shù)的方法,這個參數(shù)可以通過設(shè)置

options.inSampleSize = Int ;? //設(shè)置圖片采樣率
這個圖片采樣率的意思就是幾個像素點(diǎn)取一個基显,例如原圖的像素為 1000 * 1000? 設(shè)置采樣率為2
那么就是在橫向和縱向上每2個像素點(diǎn)采樣一次 最終得到一個 (1000/2)*(1000/2)的bitmap圖片加載到內(nèi)存中,從而達(dá)到占用內(nèi)存變小的目的足画;
但是我們獲取的圖片的大小是不確定的脐区,我們的控件尺寸在不同分辨率的設(shè)備上大小也不一樣;所以我們不能給一個固定的采樣率颇玷,需要根據(jù)獲取的圖片大小和控件大小來決定采樣率笨农;

計算采樣率:
options.inJustDecodeBounds =true
options中該屬性設(shè)置為true時,調(diào)用BitmapFactory的帶options的系列方法帖渠,bitmap不會載入內(nèi)存谒亦,方法返回null。但是方法會把bitmap的一些信息寫入options空郊,這樣就可以在不消耗大量內(nèi)存的情況下得到圖片資源的尺寸信息份招;
這里以?BitmapFactory.decodeStream(inputStream,null,options)? 方法為例:

val options = BitmapFactory.Options()
options.inJustDecodeBounds =true
BitmapFactory.decodeStream(inputStream,null,options)
//得到控件的寬高尺寸
val viewWidth =binding.imageView.measuredWidth
val viewHeight =binding.imageView.measuredHeight
//利用options中的outWidth 和 outHeight 參數(shù)和控件尺寸計算橫向和縱向的最合適采樣率
val x:Float = (options.outWidth/viewWidth).toFloat()
val y:Float = (options.outHeight/viewHeight).toFloat()
//下面這個是計算采樣率的策略,可以有不同的策略狞甚;

//如果控件的寬高至少有一個是小于圖片的寬高的時候才進(jìn)行采樣率計算
if ( viewHeight<options.outHeight || viewWidth<options.outWidth ) {
? ? // 取較大的那個作為采樣率锁摔,也有的策略取小的那個?
?????val s = if (x<y)? y? else? x
? ? ?options.inSampleSize = s.toInt(); //設(shè)置圖片采樣率
?????Log.e("qqq",""+ s +">>>>>>>" + s.toInt())
}

設(shè)置好采樣率后,重新把?options.inJustDecodeBounds = false 然后就可以采用上面的采樣率加載圖片到控件中了
val bitmap = BitmapFactory.decodeStream(inputStream,null,options)
imageView.setImageResource(bitmap)

這時候我以為大功告成了哼审,結(jié)果發(fā)現(xiàn)?
圖片竟然并沒有顯示出來谐腰,但是也并沒有什么報錯,第二次調(diào)用BitmapFactory.decodeStream(inputStream,null,options) 即時設(shè)置??options.inJustDecodeBounds = false?
返回的值還是null涩盾;怎么回事呢十气?查了一下說是我們需要在兩次decode 之間將流重置? inputStream.reset() 一下?

好了加上這句在運(yùn)行一下看看結(jié)果:

java.io.IOException: mark/reset not supported at java.io.InputStream.reset

?春霍?砸西?啥意思,大概就是說不支持?InputStream.reset,查了下芹枷,網(wǎng)上有朋友說是因?yàn)?/p>

當(dāng)給定的流不支持mark和reset就會報這個錯誤,解決方案是用BufferedInputStream把原來的流包一層.什么時候會出現(xiàn)這種錯誤呢?獲取到一個網(wǎng)絡(luò)流,這個網(wǎng)絡(luò)流不允許讀寫頭來回移動,也就不允許mark/reset機(jī)制.
BufferedInputStream zipTest=new BufferedInputStream(zip);

說的很明白了衅疙,于是我們就回到上面的獲取到inputStream的代碼,按他說的包裝一下:
val bufferedInputStream = new?BufferedInputStream(inputStream);
然后在 BitmapFactory.decodeStream(bufferedInputStream,null,options) 中傳入?bufferedInputStream鸳慈;
val bitmap = BitmapFactory.decodeStream(inputStream,null,options)
這下總該可以了吧1ヒ纭!走芋!

結(jié)果還是報錯啦@砼蟆!绿聘!

Caused by: java.io.IOException: Resetting to invalid mark

? ? ? ? at java.io.BufferedInputStream.reset(BufferedInputStream.java:450)

黑人問號臉嗽上??熄攘?兽愤?這個又是咋回事? 重置無效的標(biāo)記挪圾?由于之間完全不了解這塊的東西于是又搜了下資料浅萧,發(fā)現(xiàn)有篇相關(guān)的博客下的評論是這樣的:

*?The?maximum?read?ahead?allowed?after?a?call?to?the??????*?<code>mark</code>?method?before?subsequent?calls?to?the??????*?<code>reset</code>?method?fail.??????*?Whenever?the?difference?between?<code>pos</code>?????*?and?<code>markpos</code>?exceeds?<code>marklimit</code>,?????*?then?the??mark?may?be?dropped?by?setting?????*?<code>markpos</code>?to?<code>-1</code>.
mark(0)時沒有意思的通俗的說如果你mark(1000)之后再讀取1001個自己(超過1000),你就無法reset了哲思,就是你上面的錯誤洼畅。很好理解:你想重新獲取最近的1000個字節(jié),可以你已經(jīng)讀取的自己超過了它棚赔,你當(dāng)然無法回到開始的位置了帝簇。你在這里設(shè)置mark(0),之后只要調(diào)用了read方法就不能reset了靠益!

咦丧肴?好像是在說這個reset()調(diào)用之間要用mark(Int)標(biāo)記一下讀取的字節(jié)size,讀取超過了reset()就回不去了胧后;
于是我又在包裝bufferedInputStream后面加了一句:
bufferedInputStream.mark(1024*1024) //考慮到我讀的是一個圖片流應(yīng)該不小把芋浮,字節(jié)給足!?强臁纸巷!
最后再跑一下,終于成功加載出來了圖片眶痰,而且也確實(shí)做到了動態(tài)計算采樣率瘤旨!

好了總算整明白怎么回事了!記錄一下凛驮。

然后查資料的過程中還順便了解到長圖分區(qū)域加載裆站,大概是這個類:BitmapRegionDecoder
具體看這篇大神的博客?http://www.reibang.com/p/f576fd7313da

BitmapRegionDecoder

它提供了一系列靜態(tài)方法構(gòu)造實(shí)例

拿到實(shí)例后 通過 #decodeRegion() 方法条辟,傳入一個 Rect 和 一個BitmapFactory.Options 參數(shù) 即可解碼出一張我們要的圖片解碼區(qū)域就是我們 Rect 指定的范圍黔夭,拿到 Bitmap 后當(dāng)然可以為所欲為了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宏胯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子本姥,更是在濱河造成了極大的恐慌肩袍,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婚惫,死亡現(xiàn)場離奇詭異氛赐,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)先舷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門艰管,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蒋川,你說我怎么就攤上這事牲芋。” “怎么了捺球?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵缸浦,是天一觀的道長。 經(jīng)常有香客問我氮兵,道長裂逐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任泣栈,我火速辦了婚禮卜高,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘南片。我一直安慰自己篙悯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布铃绒。 她就那樣靜靜地躺著鸽照,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颠悬。 梳的紋絲不亂的頭發(fā)上矮燎,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天,我揣著相機(jī)與錄音赔癌,去河邊找鬼诞外。 笑死,一個胖子當(dāng)著我的面吹牛灾票,可吹牛的內(nèi)容都是我干的峡谊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼既们!你這毒婦竟也來了濒析?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤啥纸,失蹤者是張志新(化名)和其女友劉穎号杏,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斯棒,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盾致,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了荣暮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庭惜。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖穗酥,靈堂內(nèi)的尸體忽然破棺而出蜈块,到底是詐尸還是另有隱情,我是刑警寧澤迷扇,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布百揭,位于F島的核電站,受9級特大地震影響蜓席,放射性物質(zhì)發(fā)生泄漏器一。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一厨内、第九天 我趴在偏房一處隱蔽的房頂上張望祈秕。 院中可真熱鬧,春花似錦雏胃、人聲如沸请毛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽方仿。三九已至,卻和暖如春统翩,著一層夾襖步出監(jiān)牢的瞬間仙蚜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工厂汗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留委粉,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓娶桦,卻偏偏與公主長得像贾节,于是被迫代替她去往敵國和親汁汗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容