android 图片加载

来源:互联网 发布:燕十八js高手之路 编辑:程序博客网 时间:2024/04/29 03:31

1.首先解析一下基本的知识

位图模式,bitmap颜色位数是1位

灰度模式,bitmap颜色位数是8位,和256色一样

RGB模式,bitmap颜色位数是24位 在RGB模式下,一个像素对应的是红、绿、蓝三个字节

CMYK模式,bitmap颜色位数是32位  在CMYK模式下,一个像素对应的是青、品、黄、黑四个字节

图像文件的字节数(Byte) =图像分辨率*颜色深度/8(bit/8)

例如:一幅640*480图像分辨率、RGB色一般为24位真彩色,图像未经压缩的数据容量为:640X480X24/8921600字节=900KB1KBl千字节=1024字节)。
注:一个图像文件占的磁盘空间大小还和磁盘的文件格式有关。如:NTFS最小单位为4KB 所以图像文件大小肯定是4KB的倍数。但是有图图片压缩算法的存在,图片文件在保存时,体积要比在内存的大小小得多,如640x480的图片文件大小一般只在200K~300K。这也是为什么,加载几MB的图片文件,会导致JVM内存溢出,导致OutofMemoryException的原因。由上面的公式,我们可以得出,加载的图片所占的内存大小,取决于其分辨率颜色数

2. Android虚拟机(DVM)内存分配

堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。可以看到三个参数:max heap size, min heap size, heap utilization(堆利用率)。

Max Heap Size,是堆内存的上限值,Android的缺省值是16M(某些机型是24M),对于普通应用这是不能改的。函数setMinimumHeapSize其实只是改变了堆的下限值,它可以防止过于频繁的堆内存分配,当设置最小堆内存大小超过上限值时仍然采用堆的上限值(16M),对于内存不足没什么作用。VMRuntime.getRuntime().setTargetHeapUtilization 可以设定内存利用率的百分比,当实际的利用率偏离这个百分比的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。由于每个app都有最大堆内存的限制,因此加载图片时,如果超出这个限制,则会报OutofMemoryException

3.android加载图片的方式

1)通过java层分配内存

Resources.getDrawable

BitmapFactory.decodeResource

Bitmap.createBitmap

Bitmap.createScaledBitmap

通过Java层来createBitmap来完成图片的加载,增加了java层的内存消耗。使得app很容易就超出最大堆内存的限制。

2)通过native本地代码分配内存

BitmapFactory.decodeFile

BitmapFactory.decodeStream

直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。

 

a.解析图片宽高:

BitmapFactory.Options options = newBitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeStream(stream, null,options);

通过此方式,我们可以解析出图片的宽高,而不必把整张图片加载到内存,然后将options传给computeSampleSize计算采样频率,根据计算得到的inSampleSize加载图片,这样可以节省内存。

b. 计算采样频率

public static int computeSampleSize(BitmapFactory.Options options,
            int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength,
                maxNumOfPixels);


        int roundedSize;
        if (initialSize <= 8 ) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }


        return roundedSize;
    }
    
    public static int computeInitialSampleSize(BitmapFactory.Options options,
            int minSideLength, int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;


        int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
                (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
                (int) Math.min(Math.floor(w / minSideLength),
                Math.floor(h / minSideLength));


        if (upperBound < lowerBound) {
            // return the larger one when there is no overlapping zone.
            return lowerBound;
        }


        if ((maxNumOfPixels == UNCONSTRAINED) &&
                (minSideLength == UNCONSTRAINED)) {
            return 1;
        } else if (minSideLength == UNCONSTRAINED) {
            return lowerBound;
        } else {
            return upperBound;
        }
    }


c.实际加载图片:

FileInputStream fs = new FileInputStream(f);
BufferedInputStream bs = new BufferedInputStream(fs,16348);
// decode image size
final BitmapFactory.Options options = new BitmapFactory.Options();
       options.inScaled = false;
       options.inDither = true;
       //options.inSampleSize = scale;
       options.inSampleSize = ImageUtils.computeSampleSize(bs, 
        MAX_IMAGE_HEIGHT, MAX_IMAGE_WIDTH);
       FileInputStream fbmp = new FileInputStream(f);
Bitmap bmp = BitmapFactory.decodeStream (fbmp, null, options);
原创粉丝点击