Android实践:如何高效加载Bitmap
16lz
2021-01-25
一、BitmapFactory.Options简介
在Android开发中,加载图片过多、过大很容易引起OutOfMemoryError异常,即我们常见的内存溢出。因为Android对单个应用施加内存限制,默认分配的内存只有几M(具体视不同系统而定)。而载入的图片如果是JPG之类的压缩格式(JPG支持最高级别的压缩,不过该压缩是有损的),在内存中展开会占用大量的内存空间,也就容易形成内存溢出;
1. 降低图片加载到内存的分辨率(BitmapFactory.Options.inJustDecodeBounds/outWidth/outHeight/inSampleSize属性);
2. 采用更节更节省内存的编码,如ARGB_4444(BitmapFactory.Options.inPreferredConfig属性);
3. 采用缓存;
这里我们就从方法1和方法2进行处理,该方式需要了解BitmapFactory.Options,先介绍如下:
二、BitmapFactory.Options实践
1.为了跟大家更好的体会和展示优化过程,首先我们先使用一个简单Demo,使用Gallery来展示几张图片来模拟OOM,代码如下:
BitmapActivity.java:
再看看Monitor中,在两张图片加载图片过程中,内存两次迅速上升,达到200M后OOM崩溃;
3.接下来下面我们就使用BitmapFactory.Options来优化该OOM问题:
BitmapActivity.java
5.在实际的应用过过程中,inSampleSize计算并不会那么“理想 ”。比如ImagView的大小是100*100像素,而图片的原始大小是200*300呢?inSampleSize为2,则缩放后的图片大小为100*150像素,仍然是适合的;但是为3那么缩小后的图片大小就会小于ImageView的期望大小,这样图片就会拉伸从而导致模糊。下面我们就提供一种计算inSampleSize的计算方式,供大家参考:
QProject:https://github.com/Pengchengxiang/QProject 分支:feature/bitmapoption
新技术,新未来!欢迎大家关注 “1024工场”微信服务号 ,时刻关注我们的最新的技术讯息! (甭客气!尽情的扫描或者长按!)
在Android开发中,加载图片过多、过大很容易引起OutOfMemoryError异常,即我们常见的内存溢出。因为Android对单个应用施加内存限制,默认分配的内存只有几M(具体视不同系统而定)。而载入的图片如果是JPG之类的压缩格式(JPG支持最高级别的压缩,不过该压缩是有损的),在内存中展开会占用大量的内存空间,也就容易形成内存溢出;
那么高效的加载Bitmap是很重要的事情。Bitmap在Android中指的是一张图片,可以是png格式也可以是jpg等常见的格式。BitmapFactory提供了如下四类方法,可分别用于从文件系统、资源、输入流以及字节数组中加载出一个Bitmap对象
1.decodeFile;
2.decodeResource;
3.decodeStream;
4.decodeByteArray;
1. 降低图片加载到内存的分辨率(BitmapFactory.Options.inJustDecodeBounds/outWidth/outHeight/inSampleSize属性);
2. 采用更节更节省内存的编码,如ARGB_4444(BitmapFactory.Options.inPreferredConfig属性);
3. 采用缓存;
这里我们就从方法1和方法2进行处理,该方式需要了解BitmapFactory.Options,先介绍如下:
参数 | 说明 | 备注 |
inJustDecodeBounds | 为true时,解码不会返回bitmap,只会返回bitmap的尺寸。 | 用于获取图片的尺寸,但有不想将其加载到内存中。 |
inSampleSize | 当<1时,当做1处理;>1时会按照比例缩小bitmap的宽高,降低分辨率。 | 如with=100,height=100,inSampleSize=2,则返回width=50,height=50,像素50*50=250降为1/4; |
inPreferredConfig | 色彩模式,默认ARGB_8888,一个像素4bytes。如果对透明不做要求,采用RGB_565,一个像素2bytes; | ALPHA_8:每个像素1byte; ARGB_444:每个像素2byte; ARGB_8888:每个像素4byte; RGB_565:每个像素2byte; 如果一个图片分辨率1024*768,采用ARGB_8888,占用空间为1024*768*4=3M,而采用ARGB_444内存就能减半1.5M; |
inPremultiplied | 和透明通道有关,默认true,返回的bitmap颜色通道上预先附加透明通道; | 透明通道是计算机图形学术语,指的是“非彩色”通道,8位灰度通道,使用256级灰度来记录图像中的透明信息,定义透明、不透明和半透明。如32位存储的图片,8红+8绿+8蓝+8透明; |
inDither | 抖动解码,默认false,标识不采用抖动解码; | Bitmap解码是根据它所记录的节点,按照一定的算法来补充两个节点之间的数据,可理解为补充像素点的颜色。一张颜色丰富的图用一个位数比较低的颜色模式解码的话,会感觉颜色不够用,颜色渐变区域有明显断裂带。因为一些丰富的颜色在位数较低的颜色模式下并没有,只能用相近的颜色补充,可能一大片没有,那么这大片都用一个颜色填充,就形成了断裂色带;如果采用抖动解码,就会在这些颜色上采用随机噪声色来填充,这样显示效果更好,色带不那么明显。如果不想有这些色带,就需要采用抖动解码; |
inDensity | 表示这个bitmap的像素密度; | 对应DisplayMetrics.densityDpi,不是density |
inTargetDensity | 表示要被画出来时的目标像素密度; | |
inScreenDensity | 标识实际设备的像素密度; | inDensity,inTargetDensity,inScreenDensity这三个值的目的就是为了确定这个Bitmap的宽高和density。详细算法可以查看setDensityFromOptions()方法源码实现; |
inScaled | 设置这个bitmap是否可以被缩放,默认true; | |
inPurgeable/inInputShareable | 一般一起使用,设置为true时,表示空间不够可以被释放,后者表示是否可以共享引用。Android5.0后被弃用; | |
outWidth/outHeight | 表示bitmap的宽和高,一般和inJustDecodeBounds一起使用获取Bitmap的宽高,但不加载到内存中; |
二、BitmapFactory.Options实践
1.为了跟大家更好的体会和展示优化过程,首先我们先使用一个简单Demo,使用Gallery来展示几张图片来模拟OOM,代码如下:
BitmapActivity.java:
public class NextActivity extends AppCompatActivity { private int[] images = new int[]{R.drawable.p1, R.drawable.p2, R.drawable.p3}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_next); Gallery gallery = (Gallery) findViewById(R.id.gallery); gallery.setAdapter(new GalleryAdapter()); } class GalleryAdapter extends BaseAdapter { @Override public int getCount() { return images.length; } @Override public Object getItem(int position) { return images[position]; } @Override public long getItemId(int position) { return images[position]; } @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView = new ImageView(BitmapActivity.this); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), images[position]); imageView.setImageBitmap(bitmap); return imageView;le = true; } }}
2.运行后即OOM异常崩溃,错误日志输出如下: 再看看Monitor中,在两张图片加载图片过程中,内存两次迅速上升,达到200M后OOM崩溃;
3.接下来下面我们就使用BitmapFactory.Options来优化该OOM问题:
BitmapActivity.java
public class BitmapActivity extends AppCompatActivity { ... ... class GalleryAdapter extends BaseAdapter { ... ... @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView = new ImageView(BitmapActivity.this); BitmapFactory.Options options = new BitmapFactory.Options(); //inJustDecodeBounds为true,不返回bitmap,只返回这个bitmap的尺寸 options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), images[position], options); //利用返回的原图片的宽高,我们就可以计算出缩放比inSampleSize,获取指定宽度为300像素,等长宽比的缩略图,减少图片的像素 int toWidth = 300; int toHeight = options.outHeight * toWidth / options.outWidth; options.inSampleSize = options.outWidth / toWidth; options.outWidth = toWidth; options.outHeight = toHeight; //使用RGB_565减少图片大小 options.inPreferredConfig = Bitmap.Config.RGB_565; //释放内存,共享引用(21版本后失效) options.inPurgeable = true; options.inInputShareable = true; //inJustDecodeBounds为false,返回bitmap options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeResource(getResources(), images[position], options); imageView.setImageBitmap(bitmap); return imageView; } }}
4.程序正常运行,Monitor监控内存减少至21M左右!!! 5.在实际的应用过过程中,inSampleSize计算并不会那么“理想 ”。比如ImagView的大小是100*100像素,而图片的原始大小是200*300呢?inSampleSize为2,则缩放后的图片大小为100*150像素,仍然是适合的;但是为3那么缩小后的图片大小就会小于ImageView的期望大小,这样图片就会拉伸从而导致模糊。下面我们就提供一种计算inSampleSize的计算方式,供大家参考:
public static int caculateInSampleSize(BitmapFactory.Options,int reqWidth,int reqHeight){ final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if(height > reqHeight || width > reqWidth){ final int halfHeight = height / 2; final int halfWidth = width / 2; while(((halfHeight / inSampleSize) >= reqHeight) && ((halfWidth / inSampleSize) >=reqWidth)){ inSampleSize *= 2; } } return inSampleSize;}
6.代码库QProject:https://github.com/Pengchengxiang/QProject 分支:feature/bitmapoption
新技术,新未来!欢迎大家关注 “1024工场”微信服务号 ,时刻关注我们的最新的技术讯息! (甭客气!尽情的扫描或者长按!)
更多相关文章
- Android(安卓)Bitmap图片处理,防止内存溢出
- android属性中的px,sp,dip有什么区别
- 探索Android中的Parcel机制(上) .
- GC机制,你真的了解吗?
- Android(安卓)模拟器创建参数说明
- Android(安卓)Studio使用profile简单优雅的查看内存变化
- Android中动态显示gif图片
- 使用Valgrind找出Android中Native程序内存泄露问题
- android onPause()和onStop()区别