偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Bitmap 比你想的更費(fèi)內(nèi)存 | 吊打 OOM

開發(fā) 開發(fā)工具
在一個(gè) App 中,無(wú)可避免的會(huì)有一些 Bitmap 的資源,會(huì)被打包在 apk 中,隨著 apk 發(fā)布出去。而當(dāng)你在使用這些 Bitmap 的資源的時(shí)候,它到底需要占用多少內(nèi)存空間?這是一個(gè)很實(shí)際的問題,把握不好就可能引發(fā)各種 OOM 的錯(cuò)誤。

[[201248]]

一、前言

在一個(gè) App 中,無(wú)可避免的會(huì)有一些 Bitmap 的資源,會(huì)被打包在 apk 中,隨著 apk 發(fā)布出去。而當(dāng)你在使用這些 Bitmap 的資源的時(shí)候,它到底需要占用多少內(nèi)存空間?這是一個(gè)很實(shí)際的問題,把握不好就可能引發(fā)各種 OOM 的錯(cuò)誤。

本文就來探討一下,本地的 Bitmap 到底占用多少內(nèi)存空間?

二、占用多少內(nèi)存?

2.1 如何獲取占用的內(nèi)存空間?

既然需要說道一個(gè) Bitmap 資源,加載到內(nèi)存中所要占用的空間,那就需要有一個(gè)明確的獲取方法,來確定的知道它到底占用了多少空間。而 Android 確實(shí)也為我們提供了類似的 API,那就是 Bitmap.getByteCount() 。

例如,現(xiàn)在項(xiàng)目?jī)?nèi)有一個(gè) 400 * 200 像素的圖片,方在 drawable-xhdpi 目錄下,在Nexus 6 設(shè)備上,運(yùn)行加載它??此敵龅某叽?。

看一下輸出的結(jié)果:

  1. I/cxmyDev: byteCound : 720000 

可以看到,getByteCount() 是根據(jù) getRowBytes() * getHeight() 計(jì)算出來的。getHeight() 方法它是 Bitmap 的高度,而 getRowBytes() 又是什么?

2.2 getRowBytes() 的計(jì)算依據(jù)

getRowBytes() 方法,最終調(diào)用的是一個(gè) nativeRowBytes() 的方法,它是一個(gè)native 的方法。

既然要查就查到底,看看 native 的代碼是如何實(shí)現(xiàn)的(文內(nèi) native 的源碼,都是基于Android 5.1.1,文末會(huì)有在線查看地址,并且已經(jīng)附帶行號(hào),方便查閱)。

先看看 Bitmap.cpp 的代碼中 rowBytes() 是如何實(shí)現(xiàn)的。

這里閱讀的是 Android 5.1.1 的源碼,實(shí)際上從 Android 6 開始,會(huì)使用 LocalScopedBitmap 去操作,它其實(shí)也只是對(duì) SkBitmap 做了一個(gè)封裝而已。如下圖所示,rowBytes() 是使用的 LocalScoopedBitmap 來操作的,有興趣的可以繼續(xù)看看它是如何實(shí)現(xiàn)的。

可以看到,最終使用的是 SkBitmap 去實(shí)現(xiàn)的。

在 SkBitmap.cpp 里就可以確認(rèn) ,色彩度為 ARGB_8888 圖片,每像素會(huì)占用 4 bytes 的大小。

看這個(gè)樣子,結(jié)合前面提到的 Bitmap.getByteCount() 的計(jì)算公式就是:

  1. bitmapInRam = bitmapWidth * 4 bytes * bitmapHeight 

但是如果依據(jù)這樣的公式計(jì)算一個(gè)結(jié)果,你會(huì)發(fā)現(xiàn)獲得的值會(huì)比真實(shí)的值差了很多。

前面 Demo 中的圖片,加載到內(nèi)存中,占用的內(nèi)存是:720000 。但是用我們這里得到的計(jì)算方式,計(jì)算的結(jié)果是。

  1. 400 * 200 * 4 = 320000 

那么,問題出在哪里?

2.3 density 影響 Bitmap 內(nèi)存

2.1 中的 Demo ,明確指出了需要圖片存放的 Drawable 目錄,以及使用的設(shè)備,其實(shí)它們都是有關(guān)系的,不是無(wú)關(guān)系的路人甲。

關(guān)于圖片而言,放在不同的 Drawable 目錄下,對(duì)應(yīng)的不同 density 的設(shè)備。density 是設(shè)備的固有參數(shù),伴隨著 density 的,還有 densityDpi,它也是與設(shè)備相關(guān)的,表示屏幕每英寸對(duì)應(yīng)多少個(gè)點(diǎn)(非像素點(diǎn))。

它們之間的關(guān)系,可以直接查閱官方文檔,這里就不贅述了。

https://developer.android.com/guide/practices/screens_support.html

這里說到的 density ,其實(shí)就是代表不同的 drawable-xxx 目錄。

上面是官方提供的一張比較經(jīng)典的圖,可以看到,不同的目錄,代表不同的 density ,例如 xhdpi 代表的 density 就是 2。而這里的 density 對(duì) densityDip 的基準(zhǔn)是 160 ,也就是說,mdpi 對(duì)應(yīng)的 densityDpi 是 160 ,xhdpi 對(duì)應(yīng)的 densityDpi 是 320。

它們的關(guān)系如下表:

density 和 densityDpi 在 Android 中,都有標(biāo)準(zhǔn)的 API 可以拿到,利用 DisplayMetrics即可。

看到 Nexus 5 輸出的結(jié)果:

  1. I/cxmyDev: density : 3.0 
  2. I/cxmyDev: densityDpi : 480 

了解了設(shè)備的 density 和 densityDpi ,在繼續(xù)看看加載 Bitmap 的過程,使用的是 BitmapFactory.decodeResource() 方法。

從源碼上可以看出,它實(shí)際上是分兩步完成的。

  1. 使用 openRawResource() 方法獲取圖片的原始流。
  2. 使用 decodeResourceStream() 方法,對(duì)數(shù)據(jù)流進(jìn)行解碼和適配。

對(duì)于一個(gè)文件流而言,在這里我們是不需要關(guān)心的。主要影響圖片內(nèi)存的是 decodeResourceStream() 方法中,對(duì)數(shù)據(jù)流進(jìn)行解碼和適配的時(shí)候,都做了哪些處理。

在這個(gè)方法中,會(huì)傳遞一個(gè) Options 的對(duì)象,用于配置當(dāng)前圖片的解碼和適配。

從代碼中可以了解到,影響圖片內(nèi)存占比的因素有 inDensity 和 inTargetDensity 兩個(gè)。

Options 中這兩個(gè)值,都是可以設(shè)置的,如果不對(duì)其進(jìn)行額外的操作,它們默認(rèn)情況下,分別表示的含義:

  • inDensity :圖片存放的 Drawable 文件夾代表的 densityDpi 。
  • inTargetDensity : 當(dāng)前設(shè)備固有的 densityDpi 。

而使用他們的代碼,都是在 native 中,繼續(xù)追看 BitmapFactory.cpp 的源碼(源碼太多,只貼關(guān)鍵點(diǎn))

可以看到,它實(shí)際上是會(huì)通過兩個(gè) density 計(jì)算出一個(gè)比例值 scale ,它會(huì)去對(duì)圖片原始的像素進(jìn)行 scale 表示的比例的縮放。

也就是說同一張圖片,放在不同 drawable 文件夾下的圖片,在不同的設(shè)備上,實(shí)際上加載出來的尺寸也是不同的。

那計(jì)算圖片內(nèi)存的公式,就應(yīng)該調(diào)整為:

  1. scale = targetDensity / inDensity 
  2. bitmapInRam = (bitmapWidth*scale) * (bitmapHeight*scale) * 4 bytes 

再來使用新的公式,計(jì)算一下上面圖片的尺寸:

  1. 400 * (480/320) * 200 *(480/320) * 4 = 720000 

可以看到,最終得出的和我們程序中計(jì)算的值一致 了,所以這就是我們最終得到的計(jì)算圖片在內(nèi)存中,占比的公式了。

再改寫上面的 Demo ,把細(xì)節(jié)點(diǎn)都輸出出來。

看看我們關(guān)心的 Log 輸出:

  1. I/cxmyDev: byteCound : 720000 
  2. I/cxmyDev: rowBytes : 2400 
  3. I/cxmyDev: height : 300 
  4. I/cxmyDev: width : 600 
  5. I/cxmyDev: density : 3.0 
  6. I/cxmyDev: densityDpi : 480 

3.4 查缺補(bǔ)漏

前面舉的例子中,圖片尺寸和設(shè)備的 densityDpi 都是很規(guī)整的。但是不排除有一些比較不標(biāo)準(zhǔn)的設(shè)備,加載的圖片使用上面的計(jì)算公式,依然對(duì)不上。

這個(gè)問題,還是需要在源碼中找答案,對(duì)于不那么標(biāo)準(zhǔn)的 densityDpi 的設(shè)備而言,根據(jù)這個(gè)scale 計(jì)算出來的尺寸,可能是一個(gè) float 值,也就是存在小數(shù)的情況,而圖片的尺寸,都是以 int 類型為單位。所以 Android 為了規(guī)避這樣的問題,做了個(gè)容差值(0.5),去轉(zhuǎn)換成 int 類型。

代碼依然在 BitmapFactory..cpp 中。

所以 getByteCount() 這個(gè) Api 得到的尺寸,可能和我們前面使用公式計(jì)算的尺寸,略微有些偏差,這個(gè)值就是在小數(shù)點(diǎn)之間。

4、小結(jié)

好了,到這里就講清楚了一個(gè)本地的 Bitmap ,加載到內(nèi)存中,到底會(huì)占用多少內(nèi)存。

決定 Bitmap 占用內(nèi)存大小的因素,和圖片文件在磁盤上占用的空間一點(diǎn)關(guān)系都沒有,總結(jié)來說,有以下幾點(diǎn):

  • 色彩格式:比如 ARGB_8888 、RGB_5555 這種,單位像素占的內(nèi)存空間不同。
  • 圖片本身的像素尺寸。
  • 圖片文件存放的 Drawable 目錄。xhdpi 和 xxhdpi 可是不一樣的。
  • 目標(biāo)設(shè)備的 densityDpi 值。

最后附上Android 5.1.1 的相關(guān)源碼,供大家參考

Bitmap.cpp :

http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/jni/android/graphics/Bitmap.cpp

SkBitmap.cpp:

http://androidxref.com/5.1.1_r6/xref/external/skia/src/core/SkBitmap.cpp

BitmapFactory.cpp:

http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/jni/android/graphics/BitmapFactory.cpp

【本文為51CTO專欄作者“張旸”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過微信公眾號(hào)聯(lián)系作者獲取授權(quán)】

戳這里,看該作者更多好文

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2010-05-06 09:23:45

云計(jì)算

2013-12-20 09:19:18

計(jì)算機(jī)學(xué)習(xí)

2019-04-04 13:33:17

2020-04-24 09:58:18

數(shù)據(jù)泄露黑客網(wǎng)絡(luò)攻擊

2022-09-25 11:46:52

瀏覽器擴(kuò)展程序廣告攔截器

2022-09-28 07:19:35

瀏覽器安全保證惡意擴(kuò)展

2025-07-16 07:07:00

Microsoft企業(yè)云安全

2017-08-14 16:36:23

ASActivity內(nèi)存

2014-02-10 17:48:00

Windows 8.1

2012-09-20 09:28:26

PHP程序Web

2012-09-24 11:14:06

PHP編程語(yǔ)言Web開發(fā)

2015-04-13 10:30:14

2022-09-19 15:50:10

物聯(lián)網(wǎng)安全工業(yè)4.0

2023-02-10 08:13:56

Pythonf-strings

2018-04-10 16:24:03

算法分布式一致性

2021-06-09 15:40:47

容器

2022-03-31 10:39:07

Linuxsudo命令

2023-09-25 14:48:24

Wi-Fi 6

2019-01-11 10:00:44

微信騰訊改版

2024-09-27 09:53:22

Rust標(biāo)準(zhǔn)庫(kù)優(yōu)化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)