由一次引導(dǎo)頁讀取圖片引發(fā)OOM后的思考

一般我們做引導(dǎo)頁的邏輯是:

1.先判斷是否第一次啟動(dòng)app问词,如果是础浮,則進(jìn)入功能使用導(dǎo)航(最簡(jiǎn)單的做法就是,左右滑動(dòng)切換查看沽瞭,滑動(dòng)到最后一頁點(diǎn)擊按鈕進(jìn)入首頁)迁匠。

2.如果不是,則顯示啟動(dòng)屏秕脓,2秒之后進(jìn)入首頁柒瓣。

如果有廣告怎么辦?廣告從服務(wù)器拿吠架,緩存到本地,沒網(wǎng)的時(shí)候可以顯示搂鲫,或者使用webView來顯示廣告傍药。自己寫的話用ViewPager即可: ?www.reibang.com/p/adb21180862a

但我在逛github時(shí),發(fā)現(xiàn)了一個(gè)不錯(cuò)的控件:2000+star? 使用也很方便

地址:github.com/bingoogolapple/BGABanner-Android

由于項(xiàng)目需要加載本地圖片,需要在此處:

BGABanner-Android 加載本地圖片

OK 在自己手機(jī)上測(cè)試非常流暢 在蒲公英上部署完版本后我就去忙別的事了??

可是不一會(huì)項(xiàng)目經(jīng)理說在他手機(jī)上應(yīng)用打不開魂仍,直接崩潰 ?

早期在構(gòu)建項(xiàng)目時(shí)我引入了bugly拐辽,于是立刻看bugly后臺(tái),必須說騰訊出的bugly確實(shí)很好用? 報(bào)錯(cuò)一目了然 還有解決方案


OOM 內(nèi)存溢出


問題一目了然擦酌,加載圖片時(shí)占用內(nèi)存過多直接崩潰俱诸,發(fā)生OOM了,我的手機(jī)內(nèi)存空間比較大赊舶,躲避了OOM,但內(nèi)存小一些的手機(jī)就崩潰了睁搭,其實(shí)加載網(wǎng)絡(luò)圖片也容易發(fā)生OOM赶诊,不過在此處是另外一種情況,對(duì)于此园骆,解決方式很多:

1.適當(dāng)調(diào)整圖像大小舔痪。?

我推薦使用 熊貓壓縮 tinypng.com/??

熊貓壓縮網(wǎng)站

直接在網(wǎng)站上壓縮,方便快捷锌唾,圖片不失真锄码,效果顯著,可一次上傳多張圖片

2.采用合適的緩存策略。

一般用于首次加載通過網(wǎng)絡(luò)加載晌涕,獲取圖片滋捶,然后保存到內(nèi)存和 SD 卡中。

之后運(yùn)行 APP 時(shí)余黎,優(yōu)先訪問內(nèi)存中的圖片緩存炬太。

如果內(nèi)存沒有,則加載本地 SD 卡中的圖片驯耻。

具體的緩存策略可以是這樣的:內(nèi)存作為一級(jí)緩存亲族,本地作為二級(jí)緩存,網(wǎng)絡(luò)加載為最后可缚。其中霎迫,內(nèi)存使用 LruCache ,其內(nèi)部通過 LinkedhashMap 來持有外界緩存對(duì)象的強(qiáng)引用帘靡;對(duì)于本地緩存知给,使用 DiskLruCache。加載圖片的時(shí)候描姚,首先使用 LRU 方式進(jìn)行尋找涩赢,找不到指定內(nèi)容,按照三級(jí)緩存的方式轩勘,進(jìn)行本地搜索筒扒,還沒有就網(wǎng)絡(luò)加載。

代碼:www.reibang.com/p/05132e3b7320

3.采用低內(nèi)存占用量的編碼方式

比如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省內(nèi)存绊寻。

1920*1200的圖片:

ARGB_8888:1920*1200*4/1024/1024=8.79MB

ARGB_4444,RGB565:1920*1200*2/1024/1024=4.39MB

但是色彩質(zhì)量會(huì)下降花墩。

4.及時(shí)回收Bitmap

一般而言,回收bitmap內(nèi)存可以用到以下代碼

if(!bitmapObject.isRecyled()) {? // Bitmap對(duì)象沒有被回收

bitmapObject.recycle();? // 釋放

System.gc();? // 提醒系統(tǒng)及時(shí)回收

}

bitmap.recycle()方法用于回收該bitmap所占用的內(nèi)存澄步,接著將bitmap置空冰蘑,最后,別忘了用System.gc()調(diào)用一下系統(tǒng)的垃圾回收器村缸。

在這里要聲明一下祠肥,bitmap可以有多個(gè)(以為著可以有多個(gè)if語句),但System.gc()最好只有一個(gè)(所以我將它寫在了if語句外)梯皿,因?yàn)镾ystem.gc()

每次調(diào)用都要將整個(gè)內(nèi)存掃描一遍仇箱,因而如果多次調(diào)用的話會(huì)影響程序運(yùn)行的速度县恕。為了程序的效率,我將它放在了所有回收語句之后工碾,

這樣已經(jīng)起到了它的效果弱睦,還節(jié)約的時(shí)間。

回收bitmap已經(jīng)知道了渊额,那么“及時(shí)”怎么理解呢况木?

根據(jù)我的實(shí)際經(jīng)驗(yàn),bitmap發(fā)揮作用的地方要么在View里旬迹,要么在Activity里(當(dāng)然肯定有其他區(qū)域火惊,但是原理都是類似的),

回收bitmap的地方最好寫在這些區(qū)域剛剛不使用bitmap了的時(shí)刻奔垦。

比如說View如果使用了bitmap,就應(yīng)該在這個(gè)View不再繪制了的時(shí)候回收屹耐,或者是在跳轉(zhuǎn)到的下一個(gè)區(qū)域的代碼中回收;

再比如說SurfaceView椿猎,就應(yīng)該在onSurfaceDestroyed這個(gè)方法中回收惶岭;

同理,如果Activity使用了bitmap,就可以在onStop或者onDestroy方法中回收......

結(jié)合以上的共同點(diǎn)犯眠,“及時(shí)回收”的原理就是在使用了bitmap的區(qū)域結(jié)束時(shí)或結(jié)束后回收按灶。

5 在manifest文件application節(jié)點(diǎn)加入android:largeHeap=“true”

設(shè)置largeHeap的確可以增加內(nèi)存的申請(qǐng)量。但不是系統(tǒng)有多少內(nèi)存就可以申請(qǐng)多少筐咧,而是由dalvik.vm.heapsize限制鸯旁。

但是作為程序員的我們應(yīng)該努力減少內(nèi)存的使用,盡量想回收和復(fù)用的方法量蕊,而不是想方設(shè)法增大內(nèi)存铺罢。當(dāng)內(nèi)存很大的時(shí)候,每次gc的時(shí)間也會(huì)長(zhǎng)一些残炮,性能會(huì)下降的韭赘。

6 使用繪制背景或者Drawable代替圖片


以上都是一些常用的解決方案,回到項(xiàng)目中我發(fā)現(xiàn)吉殃,此前為了減小apk大小辞居,在mipmap中對(duì)于引導(dǎo)圖的資源圖片,我只用了一套蛋勺。而且圖片大小我之前已經(jīng)壓縮過,每張都僅僅在200K左右鸠删,這是為什么呢抱完?

碰巧看到了這篇測(cè)試研究:關(guān)于Android中圖片大小、內(nèi)存占用與drawable文件夾關(guān)系的研究與分析 ? ?mp.weixin.qq.com/s/7I8JcjzUzDl46cfAe8yS4Q

從上面的測(cè)試結(jié)果刃泡,我們可以得出如下結(jié)論:

1.同一張圖片巧娱,放在不同目錄下碉怔,會(huì)生成不同大小的Bitmap

2.Bitmap的長(zhǎng)度和寬度越大,占用的內(nèi)存就越大

3.圖片在硬盤上占用的大小禁添,與在內(nèi)存中占用的大小完全不一樣

我們以放在drawable文件夾下面的圖片為例撮胧,加載到內(nèi)存之后,2160*3840大小的Bitmap占用的內(nèi)存為

2160 ?3840 ?4 = 3317,7600 byte = 3,2400kb = 31.640625 M

所以drawable文件夾下的App內(nèi)存占用 = 原始內(nèi)存8.31M+圖片內(nèi)存31.64M= 39.95M 老翘,與實(shí)際內(nèi)存占用39.88M存在0.1755%的誤差芹啥,在誤差范圍之內(nèi)。

先簡(jiǎn)單解釋一下上面的計(jì)算公式铺峭,長(zhǎng)*寬是圖片的像素總數(shù)墓怀,乘以4則是因?yàn)橐粋€(gè)像素占用A、R卫键、G傀履、B四個(gè)通道,每個(gè)通道占用8位莉炉,所以描述一個(gè)像素需要32位即4個(gè)字節(jié)钓账。

一個(gè)顏色通道需要8位描述,2^8=256絮宁,所以每個(gè)顏色通道就有256種狀態(tài)梆暮。如果把彩色圖轉(zhuǎn)化成灰階圖的話,也有256種狀態(tài)分割從白色到黑色之間的過渡顏色羞福。

當(dāng)然惕蹄,也并不是所有格式的圖片每個(gè)像素占用4字節(jié),這和圖片在加載時(shí)設(shè)置的Bitmap.Config有關(guān)治专,默認(rèn)的是Bitmap.Config.ARGB_8888卖陵,其他類型如下:

Bitmap.Config.ALPHA_8 此時(shí)圖片只有alpha值,沒有RGB值张峰,1個(gè)像素占用一個(gè)字節(jié)

Bitmap.Config.ARGB_4444 一個(gè)像素占用2個(gè)字節(jié)泪蔫,alpha(A)值,Red(R)值喘批,Green(G)值撩荣,Blue(B)值各占4個(gè)bites共16bites,即2個(gè)字節(jié)

Bitmap.Config.ARGB_8888 一個(gè)像素占用4個(gè)字節(jié),alpha(A)值饶深,Red(R)值餐曹,Green(G)值,Blue(B)值各占8個(gè)bites敌厘,共32bites,即4個(gè)字節(jié)台猴。這是一種高質(zhì)量的圖片格式,在電腦上普通采用。它也是Android手機(jī)上一個(gè)Bitmap的默認(rèn)格式饱狂。

Bitmap.Config.RGB_565 一個(gè)像素占用2個(gè)字節(jié)曹步,沒有alpha(A)值,即不支持透明和半透明休讳,Red(R)值占5個(gè)bites 讲婚,Green(G)值占6個(gè)bites ,Blue(B)值占5個(gè)bites,共16bites,即2個(gè)字節(jié)俊柔。對(duì)于沒有透明和半透明顏色的圖片來說筹麸,該格式的圖片能夠達(dá)到比較的呈現(xiàn)效果,相對(duì)于ARGB_8888來說也能減少一半的內(nèi)存開銷婆咸。因此它是一個(gè)不錯(cuò)的選擇竹捉。

那么為啥在硬盤上存儲(chǔ)只需要77.11k,放到內(nèi)存里面就需要30多M呢尚骄?

存放在硬盤上的圖片文件块差,會(huì)根據(jù)各自的壓縮規(guī)則進(jìn)行壓縮,比如Jpeg這種有損壓縮的圖片格式倔丈,最常使用可變字長(zhǎng)編碼的哈弗曼編碼憨闰,會(huì)使用哈弗曼樹,也就是最優(yōu)二叉樹需五,根據(jù)某些數(shù)據(jù)出現(xiàn)的頻率對(duì)數(shù)據(jù)段編碼鹉动,從而減少占用的硬盤大小。

比如說“10111”這個(gè)序列在圖片的二進(jìn)制數(shù)據(jù)中出現(xiàn)的概率最大宏邮,那我們可以用“01”來代替這一段數(shù)據(jù)泽示,原來5位的數(shù)據(jù),用2位就可以表示了蜜氨,這就是壓縮率60%械筛。當(dāng)然這只是打個(gè)比方,在實(shí)際操作中需要考慮“異前綴原則”等編碼的基本原則飒炎。

而如果把圖像讀取到內(nèi)存中就不一樣了埋哟,因?yàn)槲覀冃枰恳粋€(gè)像素都能在屏幕上顯示,所以會(huì)把每個(gè)像素點(diǎn)都加載至內(nèi)存中郎汪,不會(huì)對(duì)相同像素進(jìn)行壓縮或者是替換赤赊,所以你也應(yīng)該能明白前面提到的Bitmap占用內(nèi)存大小的計(jì)算公式的由來了。

說到這里煞赢,其實(shí)后兩個(gè)結(jié)論已經(jīng)解釋清楚了抛计,那么為什么“同一張圖片,放在不同目錄下照筑,會(huì)生成不同大小的Bitmap”呢爷辱?

如果你真的看懂了我之前寫的文章录豺,那么這個(gè)問題應(yīng)該不算問題朦肘。

我的測(cè)試設(shè)備為錘子T1饭弓,10801960,xxhdpi媒抠,所以說弟断,如果把這張放置在xxhdpi的話,應(yīng)該不會(huì)對(duì)圖像進(jìn)行放縮趴生,也就是原始大小阀趴,所以我們?cè)谇懊娴玫絛rawable-xxhdpi文件夾下,圖片大小為720 ?1280是完全可以理解的苍匆,就是圖片本身的大小刘急。

當(dāng)圖片放置在drawable-hdpi中時(shí),圖片大小為1440 * 2560浸踩,長(zhǎng)寬變?yōu)樵瓉淼膬杀妒逯@是因?yàn)椴煌直媛手g的倍數(shù)關(guān)系導(dǎo)致的,來一張圖

我們可以很明顯的看到xxhdpi是hdpi的2倍检碗,所以如果單獨(dú)放置在某個(gè)drawable文件夾据块,手機(jī)會(huì)自動(dòng)根據(jù)當(dāng)前的屏幕密度對(duì)圖片進(jìn)行放縮。

比如上面折剃,當(dāng)把圖片放置在xxxhdpi里面的時(shí)候另假,在xxhdpi的設(shè)備上,圖片長(zhǎng) = 720 ?(3/4) = 540怕犁,圖片寬 = 1280 ?(3/4) = 960边篮,這與上面的測(cè)試結(jié)果是完全一致的。

至于為什么在前面的測(cè)試中奏甫,drawable和drawable-mdpi是一樣的大小戈轿,是因?yàn)閐rawable-mdpi是系統(tǒng)默認(rèn)的像素密度,其他像素密度都以它為基數(shù)扶檐,當(dāng)只在drawable中存在圖片時(shí)凶杖,如果使用該圖片,那么將按照drawable-mdpi的放縮比例進(jìn)行放縮款筑。

結(jié)論

從上面的測(cè)試我們可以得出以下幾個(gè)結(jié)論:

當(dāng)圖片放置在不同drawable文件夾中智蝠,且只有這一張圖片時(shí),運(yùn)行設(shè)備會(huì)根據(jù)自身的屏幕密度奈梳,對(duì)圖片進(jìn)行放縮杈湾,放縮比例符合前面圖上的規(guī)則

圖片文件的大小與在內(nèi)存中占用的大小沒關(guān)系,內(nèi)存中實(shí)際占用大小與圖片分辨率攘须、像素顯示參數(shù)有關(guān)

所以漆撞,在一個(gè)App里面使用一套UI理論上應(yīng)該是沒有問題的,但是要注意

最好使用較高分辨率的切圖,并且放置在正確的drawable文件夾中浮驳,比如按照xxhdpi的分辨率進(jìn)行切圖悍汛,放置在drawable-xxhdpi中

對(duì)于可以使用.9格式的圖片,最好使用.9至会,減少資源大小

如果有條件离咐,最好提供多套UI切圖。如果只有一套切圖奉件,系統(tǒng)需要對(duì)圖片進(jìn)行壓縮宵蛀,會(huì)進(jìn)行大量運(yùn)算,影響設(shè)備性能县貌。同時(shí)术陶,在某些情況下,系統(tǒng)對(duì)圖片的壓縮會(huì)可能會(huì)出現(xiàn)鋸齒煤痕,造成信息的丟失

如果是多套切圖的話梧宫,最好不要直接用工具按照比例放縮,這樣小圖標(biāo)會(huì)丟失一些細(xì)節(jié)杭攻。當(dāng)然祟敛,這部分是美工來做的,可以讓她參考這篇文章利用PS CS6的新功能保持ICON細(xì)節(jié)飽滿完美

思考一下兆解,如果把一個(gè)本來應(yīng)該放在drawable-xxhdpi里面的圖片放在了drawable文件夾中會(huì)出現(xiàn)什么問題呢馆铁?

在xxhdpi設(shè)備上,圖片會(huì)被放大3倍锅睛,圖片內(nèi)存占用就會(huì)變?yōu)樵瓉淼?倍埠巨!


希望這篇文章對(duì)你有所幫助,就寫到這吧现拒,我去吃雞了辣垒!大吉大利!~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末印蔬,一起剝皮案震驚了整個(gè)濱河市勋桶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌侥猬,老刑警劉巖例驹,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異退唠,居然都是意外死亡鹃锈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門瞧预,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屎债,“玉大人仅政,你說我怎么就攤上這事∨杈裕” “怎么了圆丹?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)召娜。 經(jīng)常有香客問我运褪,道長(zhǎng),這世上最難降的妖魔是什么玖瘸? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮檀咙,結(jié)果婚禮上雅倒,老公的妹妹穿的比我還像新娘。我一直安慰自己弧可,他們只是感情好蔑匣,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棕诵,像睡著了一般裁良。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上校套,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天价脾,我揣著相機(jī)與錄音,去河邊找鬼笛匙。 笑死侨把,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妹孙。 我是一名探鬼主播秋柄,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蠢正!你這毒婦竟也來了骇笔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤嚣崭,失蹤者是張志新(化名)和其女友劉穎笨触,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體有鹿,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旭旭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了葱跋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片持寄。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡源梭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出稍味,到底是詐尸還是另有隱情废麻,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布模庐,位于F島的核電站烛愧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏掂碱。R本人自食惡果不足惜怜姿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望疼燥。 院中可真熱鬧沧卢,春花似錦、人聲如沸醉者。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撬即。三九已至立磁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間剥槐,已是汗流浹背唱歧。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留才沧,地道東北人迈喉。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像温圆,于是被迫代替她去往敵國(guó)和親挨摸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

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