Android实践:高效加载Bitmap
2016-11-21 17:07
447 查看
转自 http://blog.csdn.net/p106786860/article/details/53260463
一、BitmapFactory.Options简介
在Android开发中,加载图片过多、过大很容易引起OutOfMemoryError异常,即我们常见的内存溢出。因为Android对单个应用施加内存限制,默认分配的内存只有几M(具体视不同系统而定)。而载入的图片如果是JPG之类的压缩格式(JPG支持最高级别的压缩,不过该压缩是有损的),在内存中展开会占用大量的内存空间,也就容易形成内存溢出;
那么高效的加载Bitmap是很重要的事情。Bitmap在Android中指的是一张图片,可以是png格式也可以是jpg等常见的格式。BitmapFactory提供了如下四类方法,可分别用于从文件系统、资源、输入流以及字节数组中加载出一个Bitmap对象
1.decodeFile;
2.decodeResource;
3.decodeStream;
4.decodeByteArray;
如果高效的加载类图呢,其实核心就是BitmapFactory.Options来加载所需尺寸的图片。因为很多时间ImageView并没有图片原始尺寸那么大,把整个图片加载进来显然是没有必要的。我们可以使用BitmapFactory.Options从如下几种方式对图片进行采样压缩,降低内存的占有从而减少OOM的可能性:
1. 降低图片加载到内存的分辨率(BitmapFactory.Options.inJustDecodeBounds/outWidth/outHeight/inSampleSize属性);
2. 采用更节更节省内存的编码,如ARGB_4444(BitmapFactory.Options.inPreferredConfig属性);
3. 采用缓存;
这里我们就从方法1和方法2进行处理,该方式需要了解BitmapFactory.Options,先介绍如下:
二、BitmapFactory.Options实践
1.为了跟大家更好的体会和展示优化过程,首先我们先使用一个简单Demo,使用Gallery来展示几张图片来模拟OOM,代码如下:
NextActivity.java:
[java] view
plain copy
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(NextActivity.this);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), images[position]);
imageView.setImageBitmap(bitmap);
return imageView;le = true;
}
}
}
2.运行后即OOM异常崩溃,错误日志输出如下:
再看看Monitor中,在两张图片加载图片过程中,内存两次迅速上升,达到200M后OOM崩溃;
3.接下来下面我们就使用BitmapFactory.Options来优化该OOM问题:
NextActivity.java
[java] view
plain copy
public class NextActivity extends AppCompatActivity {
... ...
class GalleryAdapter extends BaseAdapter {
... ...
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(NextActivity.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的计算方式,供大家参考:
[java] view
plain copy
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
一、BitmapFactory.Options简介
在Android开发中,加载图片过多、过大很容易引起OutOfMemoryError异常,即我们常见的内存溢出。因为Android对单个应用施加内存限制,默认分配的内存只有几M(具体视不同系统而定)。而载入的图片如果是JPG之类的压缩格式(JPG支持最高级别的压缩,不过该压缩是有损的),在内存中展开会占用大量的内存空间,也就容易形成内存溢出;
那么高效的加载Bitmap是很重要的事情。Bitmap在Android中指的是一张图片,可以是png格式也可以是jpg等常见的格式。BitmapFactory提供了如下四类方法,可分别用于从文件系统、资源、输入流以及字节数组中加载出一个Bitmap对象
1.decodeFile;
2.decodeResource;
3.decodeStream;
4.decodeByteArray;
如果高效的加载类图呢,其实核心就是BitmapFactory.Options来加载所需尺寸的图片。因为很多时间ImageView并没有图片原始尺寸那么大,把整个图片加载进来显然是没有必要的。我们可以使用BitmapFactory.Options从如下几种方式对图片进行采样压缩,降低内存的占有从而减少OOM的可能性:
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的宽高,但不加载到内存中; |
1.为了跟大家更好的体会和展示优化过程,首先我们先使用一个简单Demo,使用Gallery来展示几张图片来模拟OOM,代码如下:
NextActivity.java:
[java] view
plain copy
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(NextActivity.this);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), images[position]);
imageView.setImageBitmap(bitmap);
return imageView;le = true;
}
}
}
2.运行后即OOM异常崩溃,错误日志输出如下:
再看看Monitor中,在两张图片加载图片过程中,内存两次迅速上升,达到200M后OOM崩溃;
3.接下来下面我们就使用BitmapFactory.Options来优化该OOM问题:
NextActivity.java
[java] view
plain copy
public class NextActivity extends AppCompatActivity {
... ...
class GalleryAdapter extends BaseAdapter {
... ...
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(NextActivity.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的计算方式,供大家参考:
[java] view
plain copy
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
相关文章推荐
- Android进阶练习 - 高效显示Bitmap(高效加载较大的 Bitmaps)
- 【Android Training - 09】高效地显示Bitmap图片 [ Lesson 1 - 有效率地加载大尺寸的位图]
- Android开发之高效加载Bitmap
- Android-Bitmap高效加载避免OOM
- Android高效加载Bitmap
- 基于android示例程序(bitmapfun) 高效加载图片让人无语地方
- (一)Android开发之高效加载Bitmap
- Android使用BitMap压缩图片(高效加载大图)Code+详解
- Bitmap.Options和LruCache——Android高效加载大图、多图解决方案,有效避免程序OOM
- Android开发 —— Bitmap的高效加载
- 关于android示例程序(bitmapfun)——高效加载图片的坑爹地方
- 【Android开发经验】Bitmap高效显示系列——如何有效的加载大尺寸Bitmap
- KJFrameForAndroid框架学习----高效加载Bitmap
- Android Bitmap高效加载与LruCache内存缓存
- Android之Bitmap 高效加载
- KJFrameForAndroid框架学习----高效加载Bitmap
- KJFrameForAndroid框架学习----高效加载Bitmap
- KJFrameForAndroid框架学习----高效加载Bitmap
- android开发之Bitmap的高效加载