Android面試Android進(jìn)階(十四)-Bitmap相關(guān)問題

問:drawable和mipmap的區(qū)別是什么霞溪?

答:根據(jù)官方說明:
應(yīng)用圖標(biāo)的圖片資源存放在mipmap系列文件夾中孵滞,而其余圖片存放在drawable系列文件夾中
1、mipmap紋理映射技術(shù)會(huì)將資源縮放到設(shè)備分辨率大小鸯匹,drawable會(huì)將資源縮放到設(shè)備匹配的倍數(shù)大小
2坊饶、官方推薦開發(fā)者將位圖等資源放在對(duì)應(yīng)dpi的drawable/下,而不是放在mipmap/下殴蓬。這樣各種dpi可直接找到對(duì)應(yīng)資源匿级,減少了mipmap精確適配時(shí)需要縮放計(jì)算,也不會(huì)因?yàn)閳D片縮放導(dǎo)致顯示問題
3染厅、高密度系統(tǒng)的設(shè)備去使用低密度目錄下的圖片資源時(shí)痘绎,會(huì)將圖片長(zhǎng)寬自動(dòng)放大以去適應(yīng)高密度的精度,當(dāng)然圖片占用的內(nèi)存會(huì)更大肖粮。
所以如果能提各種dpi的對(duì)應(yīng)資源那是最好孤页,可以達(dá)到較好內(nèi)存使用效果。如果提供的圖片資源有限涩馆,那么圖片資源應(yīng)該盡量放在高密度文件夾下行施,這樣可以節(jié)省圖片放大的內(nèi)存開支

打包時(shí),可以根據(jù)目標(biāo)設(shè)備打不同的dpi圖片的包上架到應(yīng)用市場(chǎng)中去凌净,節(jié)省APK應(yīng)用包體積悲龟。

問:Bitmap內(nèi)存占用怎么算的?如加載一張1080*1920的圖片冰寻,內(nèi)存占用多少须教?

答:Bitmap的內(nèi)存占用的大小是通過:

寬 * 高 * 單位像素所占字節(jié) //1080 * 1920 * 單位像素所占字節(jié)(ARGB值不同,占用字節(jié)不同)

Bitmap.Config中有四種不同的ARGB: ALPHA_8、RGB_565轻腺、ARGB_4444乐疆、ARGB_8888
ALPHA_8:每個(gè)像素占8位,沒有色彩贬养,只有透明度A-8 即10801920(8/8/1024/1024)= 1.98M
RGB_565:每個(gè)像素每個(gè)像素占16位挤土,沒有透明度 5+6+5 = 16 即10801920(16/8/1024/1024) = 3.96M
ARGB_4444:每個(gè)像素占16位,4+4+4+4 = 16 即 10801920(16/8/1024/1024) = 3.96M
ARGB_8888:每個(gè)像素占32位误算,8+8+8+8 = 32 即 10801920(32/8/1024/1024) = 7.92M

注意:加載圖片所在內(nèi)存還和圖片放置的目錄有關(guān)系:放在mdpi仰美、xhdpi之下是不一樣的。

在Android 160dpi是系統(tǒng)默認(rèn)dpi

//獲取圖片bitmap寬高代碼:
Drawable drawable = imageView.getDrawable();
if (drawable != null) {
    BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
    Bitmap bitmap = bitmapDrawable.getBitmap();
    Log.d(TAG, " width = " + bitmap.getWidth() + " height = " + bitmap.getHeight());
} 

如果原圖大卸瘛:352 * 484
在320dpi(xhdpi)設(shè)備上運(yùn)行咖杂,當(dāng)圖片放置在xhdpi中時(shí),獲取圖片寬高依然是352 * 484蚊夫。當(dāng)圖片放置在mdpi中時(shí)诉字,獲取寬高是 704 * 968,設(shè)備是320dpi的設(shè)備知纷,當(dāng)放置在mdpi時(shí)壤圃,系統(tǒng)認(rèn)為圖片需要放大,xhdpi是mdpi的兩倍琅轧,所以獲取bitmap的寬高放大了兩倍伍绳。

當(dāng)圖片都放置在xhdpi時(shí),使用320dpi(xhdpi)設(shè)備獲取圖片寬高是352 * 484鹰晨,當(dāng)使用480dpi(xxhdpi)設(shè)備獲取圖片寬高位 528 * 726墨叛,即在480dpi設(shè)備上時(shí),xhdpi下的圖片都認(rèn)為要被放大480/320(3/2)倍模蜡。

結(jié)論:
1、在同一個(gè)設(shè)備上扁凛,圖片放在依次放在由低到高的分辨率目錄中(mdpi~xxxhdpi)忍疾,圖片的 Bitmap 內(nèi)存的大小不斷減小。
2谨朝、在同一個(gè)分辨率目錄中卤妒,依次運(yùn)行在由低到高的分辨率設(shè)備上,圖片的 Bitmap 的大小不斷增加字币。

所以:如果只使用一套圖片時(shí)则披,盡量把圖片放到最大分辨率目錄中

問:系統(tǒng)如何選擇drawable進(jìn)行加載

答:Android系統(tǒng)中,在加載圖片時(shí)洗出,會(huì)根據(jù)系統(tǒng)自身的dpi設(shè)備大小優(yōu)先匹配最近的一個(gè)drawable目錄士复,如果當(dāng)前目錄沒有找到,則向上查找,一直找到nodpi阱洪,如果都沒有找到則向下開始查找(肯定能找到便贵,如果找不到編譯器就報(bào)錯(cuò)了)。如:設(shè)備hdpi 則優(yōu)先找drawable-hdpi目錄下的資源冗荸,如果沒有則向上 xhdpi承璃、xxhdpi、xxxhdpi蚌本、nodpi盔粹,都沒有的話則開始查找mdpi...

問:Bitmap導(dǎo)致的OOM如何解決

答:Android加載大圖時(shí)極容易產(chǎn)生OOM,采用壓縮算法程癌、緩存玻佩、軟引用、及時(shí)對(duì)不再使用的bitmap對(duì)象recycle釋放等方式解決席楚。
通常會(huì)有四種壓縮方案:
1咬崔、質(zhì)量壓縮:

   /**
    * 將圖片 bitmap 壓縮到指定大小 targetSize 以內(nèi) ,單位是 kb
    * 這里的大小指的是 “文件大小”,而不是 “內(nèi)存大小”
    **/
   fun compressQuality(bitmap: Bitmap, targetSize: Int, declineQuality: Int = 10): ByteArray {
        val baos = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
        var quality = 100
        while ((baos.toByteArray().size / 1024) > targetSize) {
            baos.reset()
            quality -= declineQuality
            bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos)
        }
        return baos.toByteArray()
    }

質(zhì)量壓縮通過減少圖片色彩度烦秩,不會(huì)減少圖片像素及寬高垮斯,所以不會(huì)減少加載到內(nèi)存中所占的內(nèi)存大小,只會(huì)減少圖片所占磁盤的存儲(chǔ)大小只祠,是一種有損壓縮兜蠕。

2、采樣率壓縮:Options.inSampleSize

/**
   * 將圖片 [byteArray] 壓縮到 寬度小于 [targetWidth]抛寝、高度小于 [targetHeight]
   *
   **/
  fun compressInSampleSize(byteArray: ByteArray, targetWidth: Int, targetHeight: Int): Bitmap{

        val options = BitmapFactory.Options()  
        //設(shè)置inJustDecodeBounds = true 只加載圖片寬高等信息熊杨,不加載圖片到內(nèi)存
        options.inJustDecodeBounds = true
        BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options)
        //默認(rèn)采樣率為1,采樣率 > 1, 采樣率 inSampleSize 只能為 2 的整次冪盗舰,比如:2晶府、4、8钻趋、16
        var inSampleSize = 1
        while (options.outWidth / inSampleSize > targetWidth || options.outHeight / inSampleSize > targetHeight) {
            inSampleSize *= 2
        }
        
        options.inJustDecodeBounds = false
        options.inSampleSize = inSampleSize
        val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options)
  
        return bitmap 
    }

采樣率壓縮其原理是縮放 bitmap 的尺寸川陆,采樣率inSampleSize為1時(shí)不變,2時(shí)寬高都變?yōu)樵瓉淼?/2蛮位,所占用內(nèi)存大小就會(huì)變?yōu)樵瓉淼?/4较沪,以此類推。
由于 inSampleSize 只能為 2 的整次冪失仁,所以無法精確控制大小

3尸曼、縮放壓縮:Matrix矩陣

   /**
     * 將圖片 [bitmap] 壓縮到指定寬高范圍內(nèi)
    **/
    fun compressScale(bitmap: Bitmap, targetWidth: Int, targetHeight: Int): Bitmap {
        return try {
            //計(jì)算縮放大小
            val scale = Math.min(targetWidth * 1.0f / bitmap.width, targetHeight * 1.0f / bitmap.height)
            //創(chuàng)建矩陣對(duì)象
            val matrix = Matrix()
            matrix.setScale(scale, scale)
            //利用矩陣對(duì)bitmap原始圖片進(jìn)行壓縮生成新的bitmap
            val scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true)

            scaledBitmap
        } catch (e: Exception) {
            e.printStackTrace()
            bitmap
        }
    }

縮放壓縮使用的是通過矩陣對(duì)圖片進(jìn)行縮放,縮放后圖片的 寬度萄焦、高度以及占用的內(nèi)存都會(huì)改變控轿。

4、色彩模式壓縮:Options.inPreferredConfig = Bitmap.Config.XXXX

   /**
     * 將圖片格式更改為 Bitmap.Config.RGB_565,減少圖片占用的內(nèi)存大小
    **/
    fun compressRGB565(byteArray: ByteArray): Bitmap {
        return try {
            val options = BitmapFactory.Options()
            options.inPreferredConfig = Bitmap.Config.RGB_565
            val compressedBitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options)
            compressedBitmap
        } catch (e: Exception) {
            e.printStackTrace()
            BitmapFactory.decodeByteArray(ByteArray(0), 0, 0)
        }
    }

色彩模式壓縮后圖片的寬高不會(huì)產(chǎn)生變化解幽,由于圖片的存儲(chǔ)格式改變贴见,與 ARGB_8888 相比,每個(gè)像素的占用的字節(jié)由 8 變?yōu)?4 躲株, 所以圖片占用的內(nèi)存也為原來的一半片部。

緩存
簡(jiǎn)單說一下緩存吧,后續(xù)看Glide源碼時(shí)再來了解霜定。目前來講緩存一般有內(nèi)存緩存和磁盤緩存(網(wǎng)絡(luò)緩存也不打算吧)
內(nèi)存緩存:Android SDK中提供了一個(gè)LruCache档悠,用于內(nèi)存緩存。
磁盤緩存:Android SDK中不提供磁盤緩存的類望浩,但google官方推薦的一個(gè)叫DiskLruCache算法辖所。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市磨德,隨后出現(xiàn)的幾起案子缘回,更是在濱河造成了極大的恐慌,老刑警劉巖典挑,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酥宴,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡您觉,警方通過查閱死者的電腦和手機(jī)拙寡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琳水,“玉大人肆糕,你說我怎么就攤上這事≡谛ⅲ” “怎么了诚啃?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)浑玛。 經(jīng)常有香客問我绍申,道長(zhǎng),這世上最難降的妖魔是什么顾彰? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮胃碾,結(jié)果婚禮上涨享,老公的妹妹穿的比我還像新娘。我一直安慰自己仆百,他們只是感情好厕隧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般吁讨。 火紅的嫁衣襯著肌膚如雪髓迎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天建丧,我揣著相機(jī)與錄音排龄,去河邊找鬼。 笑死翎朱,一個(gè)胖子當(dāng)著我的面吹牛橄维,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拴曲,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼争舞,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了澈灼?” 一聲冷哼從身側(cè)響起竞川,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叁熔,沒想到半個(gè)月后委乌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡者疤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年福澡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驹马。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡革砸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出糯累,到底是詐尸還是另有隱情算利,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布泳姐,位于F島的核電站效拭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏胖秒。R本人自食惡果不足惜缎患,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阎肝。 院中可真熱鬧挤渔,春花似錦、人聲如沸风题。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至眼刃,卻和暖如春绕辖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背擂红。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工仪际, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人篮条。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓弟头,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親涉茧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赴恨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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