在Android的各類APP中都被離不開各類各樣的圖片,有的圖片很大,有的圖片很小無論這樣圖片都是一種很吃內存的資源,而在Android中每一個APP所持有的資源是很是有限的,因此咱們要儘量的「摳門」一點。本着能省則省的原則,有一個 300 x 300 的圖片如今在一個100 x 100 的ImageView中是一個徹底沒必要要的事情。因此咱們爲了更節省資源和避免OOM,咱們必須對圖片進行處理。在Android中 Bitmap
就表明一個圖片資源。android
咱們記載位圖資源都是經過 BitmapFactory
進行加載的,這個類提供瞭如下的方法。數組
方法名 | 描述 |
---|---|
decodeResource | 從資源中加載圖片 |
decodeByteArray | 從byte數組中加載圖片 |
decodeFile | 從文件中加載圖片 |
decodeStream | 從流中加載資源 |
Options類是BitmapFactory的一個嵌套類,全部的對Bitmap進行特殊處理的操做同時經過這個類的參數和屬性達成的。其中一個Bitmap所佔用的內存的大小是由下面的幾個參數影響的。app
經過 inPreferredConfig 屬性能夠設置Bitmap加載的色彩模式,主要有如下幾種色彩模式ide
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 }
採樣率能夠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
- 通過採樣後的Bitmap的大小不該該小於 ImageView的大小。
- 關於採樣率的計算的最終結果並不會徹底地精確,會有一些上下的浮動。
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了吧this
頁面的佈局文件以下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: