一个图所占据的内存。有关以内存阀值的参阅可以关押这里3.7省。

默认情况下,android程序分配的堆内存大小是16,虚拟机上面的VM Heep就是安它的

直白以来Bitmap都是开被充分艰难的题目,这个题目不怕是传说被的OOM(java.lang.OutofMemoryError

一个图纸所占用的内存,比如1920*2560像从的图纸需要,1920*2560*3起码这些的内存byte

  • 外存溢出),那么Bitmap为何如此丧失,令多Android开发者所懊恼?

 

一、Bitmap引发OOM的原因

找到ImageView控件对象

出于每个机型在编译ROM时还装了一个以堆内存VM值上限dalvik.vm.heapgrowthlimit,用来限制每个应用可用之太可怜内存,超出这个最老价值将会晤报OOM。这个阀值,一般根据手机屏幕dpi大小递增,dpi越小的无绳电话机,每个应用可用最特别内存就逾低。例如我之Z3c,xhdpi的VM阀值是192M,但是到了nexus
s
hdpi上只是出那个之48M。这样,当一个activity中加载多张大图后,就非常爱OOM了。有关以内存阀值的参阅可以看这里3.7节:

调用BitmapFactory对象的decodeFile(pathName)方法,来获取一个位图对象,参数:pathName是String类型的图路径

http://static.googleusercontent.com/media/source.android.com/en//compatibility/android-cdd.pdf

把图片导入到手机的sdcard目录下面

图表分辨率越强,消耗的内存越强,当加载高分辨率图片的时节,将会十分占用内存,一旦处理不当就见面OOM。例如,一摆放500W像素的肖像的分辨率是:2592×1936。如果Bitmap使用
ARGB_8888
32个来平铺显示的话,占用的内存是2592x1936x4只字节,占用将近19M内存,my
god,加载不顶10布置这种高质量照片,应用将直挂掉,报OOM

调用ImageView对象的setImageBitmap(bitemap)方法,参数:Bitemap对象

以用ListView,
GridView等这些大量加载view的组件时,如果无合理之处理缓存,大量加载Bitmap的时,也以善吸引OOM

此刻会见报内存溢出底荒谬

二、介绍Bitmap

 

工欲善其事必先利其器,想只要快加载Bitmap,了解Bitmap是少不了的。Bitmap有几只主要之成员变量和法,下面开始介绍:

俺们得对图纸进行缩放

2.1 Bitmap.Config

手机的分辨率比如:320*480
 图片的分辨率比如:2000*4000

平摆图片Bitmap所占用的内存 =图长度 x 图片宽度 x
一个像素点占用的字节数

个别计比例,2000/320
 4000/480,按照大的特别比例进行缩放

而Bitmap.Config,正是指定单位像素占用的字节数之重点参数。

 

里,A代表透明度;R代表红色;G代表绿色;B代表蓝色。

调用重载方法BitmapFactory对象的decodeFile(pathName,opts),参数:路径,Options对象

ALPHA_8

获取BitmapFactory.Option对象,通过new Options()方法

意味着8各Alpha位图,即A=8,一个诸如素点占用1独字节,它并未颜色,只有透明度

安装Options对象的特性inJustDecodeBounds为ture,仅解析头部信息数据

ARGB_4444

博Options对象的outHeight属性,值也图的万丈

表示16个ARGB位图,即A=4,R=4,G=4,B=4,一个像素点占4+4+4+4=16员,2个字节

赢得Options对象的outWidth属性,值为图的升幅

ARGB_8888

 

表示32各项ARGB位图,即A=8,R=8,G=8,B=8,一个如素点占8+8+8+8=32个,4单字节

获取WindowManager对象,通过getSystemSerivce()方法,参数:WINDOW_SERVICE

RGB_565

调用WindowManager对象的getDefaultDisplay().getHeight()或getWidth()方法,获取宽高

表示16各类RGB位图,即R=5,G=6,B=5,它从不透明度,一个如素点占5+6+5=16各,2独字节

 

Bitmap.Config主要意图是:以何种方式像素存储。不同之配备将会影响图像的画质(色彩深度),位数更是强画质越强,显然在此地ARGB_8888凡是无与伦比占内存的。当然,画质越强吧尽管越发占内存了。

计算宽与赛之缩放比例

Tips:由于ARGB_4444底画质惨不忍睹,一般如果针对图纸并未透明度要求的讲话,可以改变成为RGB_565,相比ARGB_8888用省一半底内存开销。

看清,当比例高于1之时段,找有宽高里面的深之价值当图片缩放比例

2.1.1
配置不同Bitmap.Config在同样分辨率下之挤占内存情况

 

同摆放图片Bitmap所占据的外存 = 图片长度 x 图片宽度 x
一个像素点占用的字节数

算了比例之后

Bitmap.Config分辨率100×100底图纸占用内存的尺寸

安Options对象的习性inJustDecodeBounds为false,真解析图片

ALPHA_8100x100x1 = 10000 byte ~= 9.77 KB

安装Options对象的采样率属性inSampleSize为面计算的异常的比例

ARGB_4444100x100x2 = 20000 byte ~= 19.53 kb

调用重载方法BitmapFactory对象的decodeFile(pathName,opts),获取到Bitmap对象

ARGB_8888100x100x4 = 40000 byte ~= 39.06 KB

调用ImageView对象的setImageBitmap(bitemap)方法,参数:Bitemap对象

RGB_565100x100x2 = 20000 byte ~= 19.53 KB

 

当Android里面可以经下面的代码来安解码率

 

2.2 Bitmap.CompressFormat

exif是图表文件之条信息

自字面上了解,它的义是:Bitmap压缩格式

 

publicenumCompressFormat {    JPEG    (0),    PNG    (1),    WEBP   
(2);    CompressFormat(intnativeInt) {this.nativeInt = nativeInt;    } 
  finalintnativeInt;}

获取ExifInterface对象,通过new出来

啊,其实这参数很简短,就是依靠定Bitmap是盖JPEG、PNG还是WEBP格式来减少

 

2.3 Bitmap.compress()方法

调用ExifInterface对象的getAttribute()方法,获取图片的音,参数:tag

重磅方法来了,通过此办法,可以实现图片的回落。使用该办法要传三单参数上:CompressFormat、int类型的quality、OutputStream

 

CompressFormat

ExifInterface.TAG_DATETIME 拍摄时

指定Bitmap的压缩格式,可卜JPEG、PNG、WEBP

 

int类型的quality

ExifInterface.TAG_MODEL 摄相机

指定Bitmap的减品质,范围是0 ~
100;该值越强,画质越强。0意味着画质最差,100画质高。

代码:

OutputStream

 

指定Bitmap的字节输出流。一般用:

package com.tsh.loadbigimg;

import java.io.IOException;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.media.ExifInterface;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;

public class MainActivity extends Activity {
    private ImageView iv_img;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv_img=(ImageView) findViewById(R.id.iv_img);

    }
    //加载大图片
    public void load(View v){
        Options opts=new Options();
        opts.inJustDecodeBounds=true;
        BitmapFactory.decodeFile("/sdcard/a.jpg", opts);
        //图片的宽高
        int imgWidth=opts.outWidth;
        int imgHeight=opts.outHeight;

        //屏幕的宽高
        WindowManager wm=(WindowManager) getSystemService(WINDOW_SERVICE);
        int windowHeight=wm.getDefaultDisplay().getWidth();
        int windowWidth=wm.getDefaultDisplay().getWidth();

        int scaleX=imgWidth/windowWidth;
        int scaleY=imgHeight/windowHeight;
        System.out.println("x比例:"+scaleX);
        System.out.println("y比例:"+scaleY);
        //计算缩放比例
        int scale=1;
        if(scaleX>scaleY&&scaleY>1){
            scale=scaleX;
        }
        if(scaleY>scaleX&&scaleX>1){
            scale=scaleY;
        }
        System.out.println("比例:"+scale);
        opts.inJustDecodeBounds=false;
        opts.inSampleSize=scale;
        Bitmap bitmap=BitmapFactory.decodeFile("/sdcard/a.jpg", opts);
        iv_img.setImageBitmap(bitmap);

    }
    //读取信息
    public void read(View v){
        try {
            ExifInterface exif=new ExifInterface("/sdcard/a.jpg");
            String date=exif.getAttribute(ExifInterface.TAG_DATETIME);
            String model=exif.getAttribute(ExifInterface.TAG_MODEL);
            System.out.println("相机:"+model+";时间:"+date);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

ByteArrayOutputStream stream = new ByteArrayOutputStream();

 

//Bitmap.compress()方法public boolean
compress(CompressFormatformat,intquality,OutputStreamstream) {if(stream
== null) {        throw newNullPointerException();    }if(quality
<0|| quality >100) {        throw
newIllegalArgumentException(“quality must be 0..100″);   
}Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,”Bitmap.compress”);   
booleanresult= nativeCompress(mFinalizer.mNativeBitmap,
format.nativeInt,            quality, stream, new
byte[WORKING_COMPRESS_STORAGE]);Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);returnresult;}

2.3.1 案例:将一个Bitmap压缩成jpeg,
quality为10,代码如下:

protectedvoid onCreate(BundlesavedInstanceState) {   
super.onCreate(savedInstanceState);setContentView(R.layout.activity_test3);ImageView
iv_1 = (ImageView) findViewById(R.id.iv_1);ImageView iv_2 =
(ImageView)
findViewById(R.id.iv_2);Bitmapbmp=BitmapFactory.decodeResource(this.getResources(),
R.mipmap.test_pic);iv_1.setImageBitmap(bmp);ByteArrayOutputStreambos=
newByteArrayOutputStream();bmp.compress(Bitmap.CompressFormat.JPEG,10,bos);byte[]bytes=bos.toByteArray();bmp=BitmapFactory.decodeByteArray(bytes,0,bytes.length);iv_2.setImageBitmap(bmp);}

运行效果图:【下面那张图明显使较达平等摆设画质差了累累】

三、介绍BitmapFactory

打地方很案例的代码可以发现,获取Bitmap不是经过组织new出来的,而是通过BitmapFactory”制造”出来的。BitmapFactory是赢得Bitmap和压缩Bitmap的关键类,下面开始介绍BitmapFactory几个根本之成员变量和办法:

3.1
通过BitmapFactory解码(获取)Bitmap的几栽艺术

decodeFile()//从SD卡文件读取

Bitmapbm=BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getAbsolutePath()+”/photo.jpg”);

decodeResource()//从资源文件res读取

Bitmapbm=BitmapFactory.decodeResource(this.getResources(),
R.mipmap.test_pic);

decodeStream()//从输入流读取

Bitmapbm=BitmapFactory.decodeStream(inputStream);

decodeByteArray()//从字节数组读取

Bitmapbm=BitmapFactory.decodeByteArray(bytes,0,bytes.length);

3.2BitmapFactory.Options

BitmapFactory在运方式decodeFile()、decodeResource()解码图片时,可以指定它的BitmapFactory.Options。此参数作用特别大,它可以设置Bitmap的采样率,通过改动图片的宽、高度、缩放比例等,以达成降低图片的像素的目的,这样好形成图片压缩,减少Bitmap的内存

脚列出BitmapFactory.Options的一对成员变量:

publicBitmap
inBitmap;publicbooleaninJustDecodeBounds;publicintinSampleSize;publicintinDensity;publicintinTargetDensity;publicintinScreenDensity;publicbooleaninScaled;publicintoutWidth;publicintoutHeight;publicStringoutMimeType;

瞧如此多成员变量是无是笨了?no,no,no,其实挺简单。一句子话总结:in开头的象征的虽是装有参数;out开头的意味的即使是取得有参数。比如,inSampleSize就是设置Bitmap的缩放比例、outWidth就是赢得Bitmap的莫大。

3.2.1 inJustDecodeBounds
设置单独去念图片的叠加信(宽高),不去分析真实的Bitmap

自从字面上亮,它的义是:”设置单独解码Bitmap的界限”。那其的确的意图是什么也?

当inJustDecodeBounds设置为true的当儿,BitmapFactory通过decodeResource或者decodeFile解码图片时,将会晤回来回空(null)的Bitmap对象,这样可避免Bitmap的内存分配,但是她好回Bitmap的宽窄、高度与MimeType。

//
当inJustDecodeBounds设置也true时,获取Bitmap的大幅度、高度及MimeTypeBitmapFactory.Optionsoptions=newBitmapFactory.Options();options.inJustDecodeBounds
=true; BitmapFactory.decodeResource (getResources(),
R.id.myimage,options);intimageHeight =options.outHeight ;intimageWidth
=options.outWidth ; String imageType =options.outMimeType ;

那么这么做有何意义呢?看了脚就段代码,你就算掌握这样做生什么意思了。意义就是在于,可以预先甭来Bitmap内存,从而得到图片的松高信息,尽可能的完成节约内存。

3.2.1.1
通过BitmapFactory.Options根据手机屏幕尺寸设置图片的缩放比例

// 根据手机屏幕尺寸设置图片的缩放比例【将大图缩放】public class
TestThreadActivity3 extends Activity
{@TargetApi(Build.VERSION_CODES.KITKAT)@Overrideprotected void
onCreate(Bundle savedInstanceState){   
super.onCreate(savedInstanceState);   
setContentView(R.layout.activity_test3);    ImageView iv_1
=(ImageView)findViewById(R.id.iv_1);    ImageView iv_2
=(ImageView)findViewById(R.id.iv_2);    BitmapFactory.Options opts =
new BitmapFactory.Options();    opts.inJustDecodeBounds
=true;//只去念图片的腔信息,不去分析真实的位图Bitmap bmp =
BitmapFactory.decodeResource(this.getResources(),   
R.mipmap.test_pic2,opts);    WindowManager wm  = getWindowManager();   
int screenWidth = wm.getDefaultDisplay().getWidth();//得到屏幕的肥瘦int
screenheight =
wm.getDefaultDisplay().getHeight();//得到屏幕的冲天Log.e(“屏幕宽度:”,screenWidth+””); 
  Log.e(“屏幕高度:”, screenheight +””);    int picWidth =
opts.outWidth;// 得到图片宽度int picHeight = opts.outHeight;//
得到图片高度Log.e(“原图片高度:”,picHeight+””);    Log.e(“原图片宽度:”,
picWidth +””);//计算图片缩放比例int dx = picWidth/screenWidth;    int dy
= picHeight/screenheight;    Log.e(“dx,dy”,dx+”,”+dy+””);   
intscale=1;if(dx>=dy&&dy>=1){       
Log.e(“按照水平方向缩放:”,dx+””);scale= dx;    }if(dy>dx&&dx>=1){ 
      Log.e(“按照竖直方向缩放:”, dy +””);scale= dy;    }   
opts.inSampleSize =scale;//设置缩放比例opts.inJustDecodeBounds
=false;//真正的去解析位图bmp =
BitmapFactory.decodeResource(this.getResources(),
R.mipmap.test_pic2,opts);    int picWidth2 = opts.outWidth;//
得到图片宽度int picHeight2 = opts.outHeight;//
得到图片高度Log.e(“压缩后底图片宽度:”,picWidth2+””);   
Log.e(“压缩后底图高度:”, picHeight2 +””);   
Log.e(“压缩后的图占用内存:”,bmp.getByteCount()+””);   
iv_2.setImageBitmap(bmp);}}

俺们读取一摆放3840×2400底图纸运行结果:

原来图直接占用36M内存,如果直白设置的说话将瞬间爆裂报OOM。所以我们这里先不加载Bitmap,而是只获宽和高,待缩放后,再展开实际的加载Bitmap。

3.2.2 inSampleSize
设置图片的缩放比例(宽和强)

在此最主要讲一下以此inSampleSize。从字面上知道,它的意义是:”设置取样大小“。它的企图是:

装inSampleSize的价(int类型)后,假如设为4,则财大气粗和高都为原本的1/4,宽高都减了,自然内存为下跌了。

如图所示:

在此地参考Google官方文档来解释:

http://developer.android.com/intl/zh-cn/training/displaying-bitmaps/load-bitmap.html\#load-bitmap

什么样晓得”设置取样大小“呢?如果你认真看了地方的情节谈,聪明的乃一定懂,肯定得般配inJustDecodeBounds,先获得图片的从容、高【这个进程就是取样】,然后经获取之方便高,动态的安装inSampleSize的价值。

【当然,你啊得不动态,可以写深inSampleSize的价。比如安inSampleSize =
4的说话,一布置分辨率为2048x1536px的图像将祭inSampleSize值为4之设置来解码,产生的Bitmap大小约为512*384px。相较于完全图片占用12M之内存,这种方法只有待0.75M内存(假设Bitmap配置也ARGB_8888)。】

这边再次举例演示一个动态设置inSampleSize的案例代码,可以由此安装图片宽高来缩放图片尺寸:

publicclassTestThreadActivity3extendsActivity{@TargetApi(Build.VERSION_CODES.KITKAT)@OverrideprotectedvoidonCreate(Bundle
savedInstanceState){super.onCreate(savedInstanceState);   
setContentView(R.layout.activity_test3);    ImageView iv_1 =
(ImageView) findViewById(R.id.iv_1);    ImageView iv_2 = (ImageView)
findViewById(R.id.iv_2);    BitmapFactory.Options opts
=newBitmapFactory.Options();    opts.inJustDecodeBounds
=true;//只去读图片的叠加信,不失分析真实的位图Bitmap bmp =
BitmapFactory.    decodeResource(this.getResources(),
R.mipmap.test_pic,opts);    Log.e(“原图占用内存:”, bmp.getByteCount()
+””);    iv_1.setImageBitmap(bmp);intpicWidth = opts.outWidth;//
得到图片宽度intpicHeight = opts.outHeight;//
得到图片高度Log.e(“原图片高度:”,picHeight+””);   
Log.e(“原图片宽度:”,picWidth+””);//根据100*100之充盈高,设置缩放比例opts.inSampleSize
= calculateInSampleSize(opts,100,100);    opts.inJustDecodeBounds
=false;//真正的去解析位图bmp =
BitmapFactory.decodeResource(this.getResources(), R.mipmap.test_pic,
opts);    Log.e(“压缩后底图占用内存:”,bmp.getByteCount()+””);   
iv_2.setImageBitmap(bmp);}publicstaticintcalculateInSampleSize(       
BitmapFactory.Options options,intreqWidth,intreqHeight){finalintheight =
options.outHeight;finalintwidth = options.outWidth;intinSampleSize
=1;if(height > reqHeight || width > reqWidth) {finalinthalfHeight
= height /2;finalinthalfWidth = width /2;while((halfHeight /
inSampleSize) > reqHeight                && (halfWidth /
inSampleSize) > reqWidth) {            inSampleSize *=2;        }   
}    Log.e(“inSampleSize:”,inSampleSize+””);returninSampleSize;}}

将图纸压缩成100x100px分辨率的运转结果:

3.2.3 inBitmap 重用Bitmap

inBitmap的基本点作用是复用之前bitmap在内存中申请的内存,其实就是对准象池的法则,以化解对象往往创建再回收的频率问题。

应用inBitmap前,每创建一个bitmap需要把一片内存

采用inBitmap后,多独bitmap会复用同一片内存

因此采用inBitmap能够大大提高内存的利用效率,但是她呢产生几只限标准:

inBitmap只会以3.0过后使用。在2.3上,bitmap的多少是储存于native
C的内存区域,并无是当java dalvik的内存堆上。

在SDK 11 ->
18间,重用的bitmap大小要是一样的,例如给inBitmap赋值的图片大小为100-100,那么新申请的bitmap必须为为100-100才会被引用。从SDK
19初步,新申请的bitmap大小要低于或者当已经赋值过之bitmap大小。

乍申请之bitmap与原来的bitmap必须来相同之解码格式,例如大家都是8888底,如果面前的bitmap是8888,那么就是非能够支撑4444跟565格式的bitmap了,不过可以经过创设一个富含多一流而选用bitmap的对象池,这样继续的bitmap创建都能找到适合的“模板”去进行录取。

下是怎使用inBitmap的代码示例:

季、总结(Bitmap压缩的几栽办法)

1、Bitmap压缩的一定量种常用方法

质量压缩法Bitmap.compress()

参照2.3.1节省的代码

抽样压缩法设置inSampleSize的值

参考3.2.1.1 和 3.2.2的代码

2、在骨子里利用中可整合质量压缩法和取样压缩法一起从而,以达到最佳压缩效果。

3、看了了当时首内容,其实说白了,Bitmap压缩都是围绕这个来举行文章:Bitmap所占用的外存
= 图片长度 x 图片宽度 x
一个像素点占用的字节数。3只参数,任意减少一个之价,就高达了削减的机能。

相关文章