Android-认识Bitmap

学习自

  • Android开发艺术探索

例行废话

在Android的各种APP中都被离不开各种各样的图片,有的图片很大,有的图片很小不管这样图片都是一种很吃内存的资源,而在Android中每个APP所持有的资源是非常有限的,所以我们要尽可能的“抠门”一点。本着能省则省的原则,有一个 300 x 300 的图片现在在一个100 x 100 的ImageView中是一个完全不必要的事情。所以我们为了更节省资源和避免OOM,我们必须对图片进行处理。在Android中 Bitmap 就代表一个图片资源。

BitmapFactory和Options

我们记载位图资源都是通过 BitmapFactory 进行加载的,这个类提供了以下的方法。

方法名 描述
decodeResource 从资源中加载图片
decodeByteArray 从byte数组中加载图片
decodeFile 从文件中加载图片
decodeStream 从流中加载资源

Options

Options类是BitmapFactory的一个嵌套类,所有的对Bitmap进行特殊处理的操作同时通过这个类的参数和属性达成的。其中一个Bitmap所占用的内存的大小是由下面的几个参数影响的。

  • inPreferredConfig Bitmap的色彩模式,不同的色彩模式,每个像素所占据的字节的数量是不一样的。
  • inSampleSize 采样率,采样率越大,Bitmap的宽和高就越小,所占用的内存也就越小。

inPreferredConfig

通过 inPreferredConfig 属性可以设置Bitmap加载的色彩模式,主要有以下几种色彩模式

  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:

  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了吧

  1. 在加载图片的时候首先获取到图片的宽和高和ImageView的宽和高(在这一步并不会加载Bitmap仅仅会获取Bitmap的宽和高)
  2. 根据图片的宽和高来和ImageView的采样率
  3. 以指定的采样率加载Bitmap

页面的布局文件如下

<?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:

Android-认识Bitmap的更多相关文章

  1. int android.graphics.Bitmap.getRowBytes()

    int android.graphics.Bitmap.getRowBytes() Return the number of bytes between rows in the bitmap's pi ...

  2. Android中Bitmap, Drawable, Byte,ID之间的转化

    Android中Bitmap, Drawable, Byte,ID之间的转化 1.  Bitmap 转化为 byte ByteArrayOutputStream out = new ByteArray ...

  3. Android笔记——Bitmap自动取色(纯搬运)

    2015/6/12更新:发现一个更好的,带demo https://github.com/MichaelEvans/ColorArt 说明: 这个是一个老外写的自动自动从bitmap中取主色与第二主色 ...

  4. android 管理Bitmap内存 - 开发文档翻译

    由于本人英文能力实在有限,不足之初敬请谅解 本博客只要没有注明“转”,那么均为原创,转贴请注明本博客链接链接   Managing Bitmap Memory 管理Bitmap内存 In additi ...

  5. Android中 Bitmap Drawable Paint的获取、转换以及使用

    比如Drawable中有一系列连续的图片,img_0.png, img_1.png, img_2.png ... 如果要动态获取这些图片,通过"R.drawable.img_x"的 ...

  6. Android 图片Bitmap,drawable,res资源图片之间转换

    一.知识介绍 ①res资源图片是放在项目res文件下的资源图片 ②BitMap位图,一般文件后缀为BMP,需要编码器编码,如RGB565,RGB8888等.一种逐像素的显示对象,其执行效率高,但缺点也 ...

  7. Android中Bitmap对象和字节流之间的相互转换

    android 将图片内容解析成字节数组,将字节数组转换为ImageView可调用的Bitmap对象,图片缩放,把字节数组保存为一个文件,把Bitmap转Byte   import java.io.B ...

  8. void android.graphics.Bitmap.recycle()

    void android.graphics.Bitmap.recycle() Free up the memory associated with this bitmap's pixels, and ...

  9. android中Bitmap的放大和缩小的方法

    android中Bitmap的放大和缩小的方法 时间 2013-06-20 19:02:34  CSDN博客原文  http://blog.csdn.net/ada168855/article/det ...

  10. 关于bitmap recycle trying to use a recycled bitmap android.graphics.Bitmap

    在开发中,一直使用4.0以上手机作为測试机所以一直没有出现这个问题,今天换了2.3版本号的手机.出现了这个错误: trying to use a recycled bitmap android.gra ...

随机推荐

  1. ubuntu 禁用自带的nouveau显卡驱动,安装NVIDIA显卡驱动

    下载显卡驱动 进入Nvidia的官网,找到对应GTX 750显卡的Linux 64-bit 的驱动程序,然后下载 当点击下载链接后,发现浏览器一直在加载那个*.run文件,很久都加载不完.这时将浏览器 ...

  2. 【逆向工具】IDA使用3-全局变量、数组、结构体

    全局变量 测试代码 全局变量既可以是某对象函数创建,也可以是在本程序任何地方创建.全局变量是可以被本程序所有对象或函数引用.下面这段代码中将int.float.char变量定义在main函数之外. / ...

  3. 在Linux,误删磁盘分区怎么恢复呢【转】

    在我们运维工作中,频繁的操作,可能命令写入错误,造成磁盘分区的删除,那么应该怎么办呢?怎么恢复磁盘分区呢? 一不小心删除了磁盘分区.如下图,删除了sda磁盘的第一个分区,为系统boot分区,系统如果重 ...

  4. sqlserver中将查询结果拼接成字符串

    #for xml path(param)--将查询结果以xml格式输出 select id,name from table1 for xml path --id和name为table1的真实字段 - ...

  5. java 基础(转自索宁)

    一.方法函数 函数也称为方法,就是定义在类中的具有特定功能的一段独立代码.用于定义功能,提高代码的复用性. 函数的特点1> 定义函数可以将功能代码进行封装,便于对该功能进行复用:2> 函数 ...

  6. oracle forall

    select * bulk collect    into r_115    from TZTJ_CL0115   where nf = v_nf     and yf = v_yf     and ...

  7. html中子界面与父界面相互操作或传值

    一.在使用iframe的页面,要操作这个iframe里面的DOM元素可以用: contentWindow.contentDocument(测试的时候chrom浏览器,要在服务器环境下) content ...

  8. vue系列之核心思想

    1.数据驱动 只要改变数据,Vuejs会通过Directives指令对DOM进行封装,当数据发生变化,会通知相应的DOM进行变化 Vuejs会对DOM进行监听,通过DOMListeners监听视图的变 ...

  9. 为什么js中0.1+0.2不等于0.3,怎样处理使之相等?(转载)

    为什么js中0.1+0.2不等于0.3,怎样处理使之相等? console.log(0.1+0.2===0.3)// true or false?? 在正常的数学逻辑思维中,0.1+0.2=0.3这个 ...

  10. Tronado

    Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效 ...