一、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的宽高,但不加载到内存中;  

二、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工场”微信服务号 ,时刻关注我们的最新的技术讯息! (甭客气!尽情的扫描或者长按!)

更多相关文章

  1. Android(安卓)Bitmap图片处理,防止内存溢出
  2. android属性中的px,sp,dip有什么区别
  3. 探索Android中的Parcel机制(上) .
  4. GC机制,你真的了解吗?
  5. Android(安卓)模拟器创建参数说明
  6. Android(安卓)Studio使用profile简单优雅的查看内存变化
  7. Android中动态显示gif图片
  8. 使用Valgrind找出Android中Native程序内存泄露问题
  9. android onPause()和onStop()区别

随机推荐

  1. Android多线程--HandlerThread用法
  2. IntentTest
  3. 获取mic音量大小
  4. android在线播放mp4/3gp
  5. 一行代码引来的安全漏洞就让我们丢失了整
  6. android禁止状态栏下拉
  7. introduction to JAVA-based open-source
  8. android app 内部文件路径
  9. Android(安卓)设置飞行模式
  10. Navigation(2)