博客为 有时个哥 原创,如需转载请标明出处:http://blog.csdn.net/ls703/article/details/40378067
在Android应用中是离不开图片的,由于手机本身资源就是有限的,所以我们时常要对图片进行压缩处理。
对图片进行操作基本上有两种吧,一种是直接从本地(手机sd卡)上获得图片,一种是从服务器获得图片。从本地获得图片,又有不同的用途,不同的用途也导致了我们对图片的处理。例如,从本地或的图片直接用于展示,这类的容易造成oom异常的情况是从图库里获得图片,有的也可能是对图库里的图片进行展示,比如有的项目需要从图库里经行多选图片发送上传,这就要自己编写界面来对图片经行展示(因为Android里系统的图库不支持图片的多选),假如你手机上的图片很多,那么就非常容易造成oom异常。假如你是上传图片,为了节省流量我们一般是要对图片经行压缩处理,然后上传,例如我们经常的用的qq,你上传图片时会有个提示,就是提示是否发送原图,如果不发送原图,就是发送的压缩之后的图片。下面我就根据自己的观点结合一些查找的资料来整理一下各个情况的图片压缩。首先要先学一下和处理图片相关的常用类的常用方法。
一,Bitmap类
Bitmap的具体概念,具体概念呢百度一搜一大把,我们就可以简单的理解为,他就是一个用来处理像素图片的一个对象类,处理图片就会常和这个类打交道。看一下Bitmap里面的方法,
1)
public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream)
compress的意思就是压缩,所以我们从名字来看就知道这是一个压缩方法,这个方法的作用是要像流中写入一个压缩版的bitmp,说白了就是一个bitmap转化为流的方法。如果返回的是true,就说明bitmap被重建成相对应的流。当然,可以利用BitmapFactory中的decodeStream()再变回bitmap,官方api是说:不是所有的格式都直接支持bitmap的配置,因此返回的bitmap可能在图像色深和图像的透明度发生变化。例如JPEG只支持不透明像素。管于图片的色深和透明度对图片的影响这里不做阐述,可以找度娘问个明白。
这个方法有三个参数,第一个参数是你的压缩格式,Bitmap.CompressFormat是bitmap里面的一个内部类,在这内部类里面有两个枚举值,也就是格式,JPEG和PNG。这个参数设置成其中一个就可以。第二个参数,quality 是设置图片的质量,0~100,表示要压缩成原来的质量的比例,,100表示图片质量的最大值,0表示最小值,假如30,则表示质量被压缩了百分之70,当让不同的格式,影响不同,假若你设置的是png格式,则质量就可以忽略,怎么设,他都是无损的。
2)
public final int getByteCount()
这个方法是4版本加上的,2版本是没有的,他的作用是的到返回一个存储像素的字节,也可以理解为bitmap占用的内存。如果要兼容低版本的话,就需要bitmap.getRowByteCount()*bitmap.getHeight()这样计算。 public final int getHeight()是得到bitmap的高度,.getRowByteCount()是得到每行的字节数,其实计算bitmap的大小也可以getWidth()*getHeight()*单位像素的所占用的字节数。
3)
public final boolean isRecycled()
判断bitmap是否被回收
4)public void recycle()
这个方法是用来回收bitmap对象的,其回收的意思就是清除其对象的索引,但不会同步的立即清除像素的数据,他是需要gc回收的,gc会自动检测没有索引的对象,回收他们,所以这个方法一般情况下是不用调用的,但有时因为bitmap过多造成oom是我们一般也会用着个方法配合gc强制回收bitmap
5)Enum Bitmap.Config
这是一个bitmap的内部类,主要是设置bitmap的config,里面有四种形式,
ALPHA_8
Each pixel is stored as a single translucency (alpha) channel.
|
ARGB_4444
Deprecated.
Because of the poor quality of this configuration, it is advised to use
ARGB_8888 instead.
|
ARGB_8888
Each pixel is stored on 4 bytes.
|
RGB_565 |
不同形式占用的字节不同,默认是ARGB——8888
bitmap还有很多其他方法,我觉得这几个是比较常用的几个方法。
二,BitmapFactory
这个类也是处理图片常用的类,下面是常用的方法
BitmapFactory里面有一个比较常用的内部类Options,这个类很重要,一般对Bitmap压缩都是使用到这个类。
1.这个options类可以理解为对在图片解码时做的一些设置。其中里面有很多字段有不同的作用。
1)Bitmap inBitmap 作用是:如过设置了这个字段,则在选择有options参数的解码方式执行时,就会重复利用这个bitmap,如果解码方法没有使用到这个bitmap,则就会返回null并抛出IllegalArgumentException.,当前的实现需求是重复利用jpeg或png的格式的bitmap作为资源内容。如果设置了它,则重复利用的bitmap的config就会使用inPreferredConfig
这个字段的,而不是以前的config。
2)Bitmap.Config inPreferredConfig 就是bitmap的配置,如果他不为空的话,BitmapFactory在解析图片的时候会按这个config解析,如果他为空时或请求无法满足的时候,他就会根据屏幕的色深去匹配最好的config,有过原始图像有透明度的话也是按这个来配置(png不支持透明度)。其默认的配置是Bitmap.Config.ARGB_8888
3)boolean inJustDecodeBounds 这个是压缩的时候常用的到的字段,设置为true的时候是可以允许查询bitmap中没有分配像素的内存,你可以简单的理解为,需要先设置这个字段,但是在设置为true的时候,返回的bitmap是为null的,所以在设置完相对应的压缩参数之后需要设置会false才能再得到bitmap对象。可以理解为这个参数不设压缩不成功
4)int inSampleSize 这个便是压缩的倍数,1是相当于和原来一样,2是压缩成原来的二分之一。这个字段是改变图像的宽和高,这个倍数也是值得宽高压缩,二像素的压缩倍数是长和宽压缩倍数的乘积。因为一个图片的尺寸对应着一个像素数。这个值最小为1,如果设置小于1,则会处理为1的效果。这个实会改变图片的规模大小的,也就是说会改变像素的数量。
5)int outWidth 要压缩后的宽度
6)int outHeihgt要压缩后的长度
这些也是options比较常用的吧
2.在BitmapFactory里面就是那么一类的方法,就是解析图片的方法
Modifier and Type | Method and Description |
---|---|
static Bitmap | decodeByteArray(byte[] data, int offset, int length)
Decode an immutable bitmap from the specified byte array.
|
static Bitmap | decodeByteArray(byte[] data, int offset, int length,BitmapFactory.Options opts)
Decode an immutable bitmap from the specified byte array.
|
static Bitmap | decodeFile(String pathName)
Decode a file path into a bitmap.
|
static Bitmap | decodeFile(String pathName,BitmapFactory.Options opts)
Decode a file path into a bitmap.
|
static Bitmap | decodeFileDescriptor(FileDescriptor fd)
Decode a bitmap from the file descriptor.
|
static Bitmap | decodeFileDescriptor(FileDescriptor fd,Rect outPadding,BitmapFactory.Options opts)
Decode a bitmap from the file descriptor.
|
static Bitmap | decodeResource(Resources res, int id)
Synonym for
decodeResource(Resources, int, android.graphics.BitmapFactory.Options) will null Options.
|
static Bitmap | decodeResource(Resources res, int id,BitmapFactory.Options opts)
Synonym for opening the given resource and calling
decodeResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options) .
|
static Bitmap | decodeResourceStream(Resources res,TypedValue value,InputStream is,Rect pad,BitmapFactory.Options opts)
Decode a new Bitmap from an InputStream.
|
static Bitmap | decodeStream(InputStream is)
Decode an input stream into a bitmap.
|
static Bitmap | decodeStream(InputStream is,Rect outPadding,BitmapFactory.Options opts)
Decode an input stream into a bitmap.
|
他们的区别就是获取资源的方式不同有的是byte数组有的直接给的路径,有的是流,但都是获得资源后解析。没啥很大的区别。
三,图片的存储形式
在开发中,图片的存储形式我觉得可分为三种
一个就是在存储设备的上的文件形式,在一个就是流的形式在内存中,在一个就是Bitmap。
但是他们之间有什么关系,转化有什么关系,各种形式的大小是否有不同。所以我用代码通过输出来看了一下他们之间的区别。
首先我在/mnt/sdcard/1.jpg 放入这个路径下一张图片,图片的大小1,425,666字节。
然后用不同形式读取,一种把他转化为流,一种是把他转化bitmap,然后在输出其占用内存的大小。关于图片内存大小,及bitmap的内存求法这里有两篇日志,我觉得说的很清楚了。http://www.2cto.com/kf/201409/338241.html和http://my.oschina.net/rengwuxian/blog/182885。
下面就是测试代码,简单的测试代码:
import java.io.FileInputStream;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getBitmap("/mnt/sdcard/1.jpg");
getInputStreanm("/mnt/sdcard/1.jpg");
}
public void getBitmap(String pathName){
// Bitmap bm = ImageUtils.getBitmapFromLocal(pathName);
Bitmap bm = BitmapFactory.decodeFile(pathName);;
System.out.println("bitmap:::"+bm.getByteCount());
System.out.println("bitmap的另一种求法:::"+bm.getWidth() * bm.getHeight()*4 );
}
public void getInputStreanm(String pathName){
try {
FileInputStream fis = new FileInputStream(pathName);
System.out.println("inputStream:::"+fis.available());
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果为:
看看流形式的时候,是和文件形式是一样大的。而成为bitmap后则占用的内存是原来的几十倍。差别很大。我觉得,图片以文件和流形式的时候都是以一种压缩的存储形式来存储的,就可能像是一盘豆子,被紧紧的压在一起,而bitmap对象,api中介绍说是把每个像素存储起来,我觉得可以理解成散开的豆子,自然占的空间大。这只是个人猜想。可能也跟存储结构有关系的,这方面也没过多涉及,所以感兴趣的可以自己去了解一下。由于变成bitmap对象占用的内存会增大,由于手机资源也是非常有限的,所以我们对bitmap的处理是有必要的。
下一篇就要整理一下,图片的一些处理了。