Android-認識Bitmap

Android-認識Bitmap

學習自

  • Android開發藝術探索

例行廢話

在Android的各類APP中都被離不開各類各樣的圖片,有的圖片很大,有的圖片很小無論這樣圖片都是一種很吃內存的資源,而在Android中每一個APP所持有的資源是很是有限的,因此咱們要儘量的「摳門」一點。本着能省則省的原則,有一個 300 x 300 的圖片如今在一個100 x 100 的ImageView中是一個徹底沒必要要的事情。因此咱們爲了更節省資源和避免OOM,咱們必須對圖片進行處理。在Android中 Bitmap 就表明一個圖片資源。android

BitmapFactory和Options

咱們記載位圖資源都是經過 BitmapFactory 進行加載的,這個類提供瞭如下的方法。數組

方法名 描述
decodeResource 從資源中加載圖片
decodeByteArray 從byte數組中加載圖片
decodeFile 從文件中加載圖片
decodeStream 從流中加載資源

Options

Options類是BitmapFactory的一個嵌套類,全部的對Bitmap進行特殊處理的操做同時經過這個類的參數和屬性達成的。其中一個Bitmap所佔用的內存的大小是由下面的幾個參數影響的。app

  • inPreferredConfig Bitmap的色彩模式,不一樣的色彩模式,每一個像素所佔據的字節的數量是不同的。
  • inSampleSize 採樣率,採樣率越大,Bitmap的寬和高就越小,所佔用的內存也就越小。

inPreferredConfig

經過 inPreferredConfig 屬性能夠設置Bitmap加載的色彩模式,主要有如下幾種色彩模式ide

  1. ALPHA_8 每一個像素佔據1byte
  2. ARGB_4444 每一個像素佔據2byte
  3. ARGB_8888 每一個像素佔據4byte
  4. RGB_565 每一個像素佔據2byte

ARGB_8888 的顯式效果最好,同時也是Android默認的色彩模式,可是也最爲消耗內存。
假設一張1024 x 1024,模式爲ARGB_8888的圖片,那麼它佔有的內存就是:
1024 x 1024 x 4 byte佈局

//ARGB_8888 模式加載的Bitmap
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.wallbackground)
Log.e("TAG", "Width:${bitmap.width} Helight:${bitmap.height}")
Log.e("TAG", "Original:" + getBitmapSize(bitmap))

//Log信息以下
//Width:5040 Helight:2835
//Size:57153600

//RGB_565 這裏本想使用 ARGB_4444 的測彩模式的,可是打印出來的一直卻和模式的ARGB_8888相同
//可能也跟具體的設備有關係吧,因此這裏就是用了 RGB_565 的色彩模式來驗證一下不一樣的色彩模式
//佔中的內存大小不一樣
val options = BitmapFactory.Options()
options.inPreferredConfig = Bitmap.Config.RGB_565
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.wallbackground,options)
Log.e("TAG", "Width:${bitmap.width} Helight:${bitmap.height}")
Log.e("TAG", "Size:" + getBitmapSize(bitmap))

//Log信息以下
//Width:5040 Helight:2835
//Size:28576800

fun getBitmapSize(bitmap: Bitmap): Int {
    return bitmap.rowBytes * bitmap.height
}

inSampleSize 採樣率

採樣率能夠Bitmap內存優化的重頭戲,上面也提到了,當使用一個大圖加載到一個小圖中的時候這種狀況是徹底不須要的,好比說 400 x 400 圖片要顯示在 200 x 200 的ImageView上的時候,那麼只須要記載200 x 200 大小的圖片就好了,而若是直接加載 400 x 400 的圖片就憑白地浪費了一倍的空間。因此咱們須要經過設置Bitmap的採樣率將圖片縮小。固然最後通過採樣後的圖片最好不要小於 ImageView 的大小,不然就會形成拉伸效果。學習

現假設要加載一個400 x 400 的圖片,採樣率默認是 1 也就是加載原圖,若是採樣變爲 2 那麼 圖片的寬高都會 / 2 因此所佔據的內存也就只有原圖的 1/4(1/採樣率的平方) 了。採樣率越大圖片的寬高越小,佔用的內存也就越小。 在官方文檔中推薦將採樣率設置爲2的N次冪(1,2,3,8..),好比說若是採樣率設爲 3 那麼採樣率就是 2 ,可是這真是一個參考,在一些機型上的運行效果並非如此。優化

NOTE:ui

  1. 通過採樣後的Bitmap的大小不該該小於 ImageView的大小。
  2. 關於採樣率的計算的最終結果並不會徹底地精確,會有一些上下的浮動。
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.wallbackground)
Log.e("TAG", "Size:" + getBitmapSize(bitmap))
//Log: Size:57153600

val options = BitmapFactory.Options()
options.inSampleSize = 2
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.wallbackground, options)
Log.e("TAG", "Size:" + getBitmapSize(bitmap))
//Log: 3573360

fun getBitmapSize(bitmap: Bitmap): Int {
    return bitmap.rowBytes * bitmap.height
}
//Log Size:14293440

優化Bitmap加載

我想經過上面的介紹你已經大概知道了若是優化Bitmap了吧this

  1. 在加載圖片的時候首先獲取到圖片的寬和高和ImageView的寬和高(在這一步並不會加載Bitmap僅僅會獲取Bitmap的寬和高)
  2. 根據圖片的寬和高來和ImageView的採樣率
  3. 以指定的採樣率加載Bitmap

頁面的佈局文件以下code

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="cn.shycoder.studybitmap.MainActivity">

    <Button
        android:id="@+id/btnLoadImg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Load Img" />

    <ImageView
        android:id="@+id/iv"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center_horizontal" />
</LinearLayout>

Activity 的代碼

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun onClick(view: View) {
        loadImageFromResource(iv, R.drawable.wallbackground)
    }

    /**
     * 從資源中獲得一個合適大小的Bitmap
     * */
    private fun loadImageFromResource(imageView: ImageView, resId: Int) {
        Log.e("TAG", "ImageView Width:${imageView.width} Height: ${imageView.height}")

        //1. 獲取Bitmap的寬高
        val options = BitmapFactory.Options()
        // 經過設置此選項,加載Bitmap的時候,僅僅會獲取寬和高並不會真正地加載Bitmap
        options.inJustDecodeBounds = true
        BitmapFactory.decodeResource(this.resources, resId, options)

        //2. 計算對應的圖片的採樣率
        val inSampleSize = this.calculateInSampleSize(options, imageView.width, imageView.height)

        //3.根據採樣率加載圖片
        //記得取消這個選項
        options.inJustDecodeBounds = false
        options.inSampleSize = inSampleSize
        val bitmap = BitmapFactory.decodeResource(this.resources, resId, options)
        imageView.setImageBitmap(bitmap)
    }

    private fun calculateInSampleSize(options: BitmapFactory.Options, requireWidth: Int,
                                      requireHeight: Int): Int {
        val width = options.outWidth
        val height = options.outHeight
        var inSampleSize = 1
        //若是Bitmap的大小是大於ImageView的大小的
        if (width > requireWidth && height > requireHeight) {
            //採樣率的增長
            while ((width / (inSampleSize + 1) > requireWidth)
                    && (height / (inSampleSize + 1) > requireHeight)) {
                inSampleSize += 1
            }
        }

        return inSampleSize
    }
}

圖片對比

// 原圖所佔內存:60466176
// 通過採樣壓縮後:15148960:

Snag_484e1caf

相關文章
相關標籤/搜索