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

相关文章
相关标签/搜索