当前位置: 代码迷 >> Android >> android 缩放图片唤起的内存溢出
  详细解决方案

android 缩放图片唤起的内存溢出

热度:59   发布时间:2016-05-01 16:42:52.0
android 缩放图片引起的内存溢出
编写:徐建祥([email protected])
日期:2010/12/06
网址:http://www.anymobile.org

传输文件,或者设置头像,我们一般都会检查原始图片的大小,作缩放处理。

常用的Java版缩放图片代码:

view plaincopy to clipboardprint?

    public Bitmap getZoomImage(Bitmap src, int desW, int desH) 
    { 
        Bitmap desImg = null; 
        int srcW = src.getWidth(); // 原始图像宽 
        int srcH = src.getHeight(); // 原始图像高 
        int[] srcBuf = new int[srcW * srcH]; // 原始图片像素信息缓存 
         
        src.getPixels(srcBuf, 0, srcW, 0, 0, srcW, srcH); 
         
        // 计算插值表 
        int[] tabY = new int[desH]; 
        int[] tabX = new int[desW]; 
         
        int sb = 0; 
        int db = 0; 
        int tems = 0; 
        int temd = 0; 
        int distance = srcH > desH ? srcH : desH; 
        for (int i = 0; i <= distance; i++) 
        {/* 垂直方向 */ 
            tabY[db] = sb; 
            tems += srcH; 
            temd += desH; 
            if (tems > distance) 
            { 
                tems -= distance; 
                sb++; 
            } 
            if (temd > distance) 
            { 
                temd -= distance; 
                db++; 
            } 
        } 
         
        sb = 0; 
        db = 0; 
        tems = 0; 
        temd = 0; 
        distance = srcW > desW ? srcW : desW; 
         
        for (int i = 0; i <= distance; i++) 
        {/* 水平方向 */ 
            tabX[db] = (short) sb; 
            tems += srcW; 
            temd += desW; 
            if (tems > distance) 
            { 
                tems -= distance; 
                sb++; 
            } 
            if (temd > distance) 
            { 
                temd -= distance; 
                db++; 
            } 
        } 
         
        // 生成放大缩小后图形像素 
        int[] desBuf = new int[desW * desH]; 
        int dx = 0; 
        int dy = 0; 
        int sy = 0; 
        int oldy = -1; 
         
        for (int i = 0; i < desH; i++) 
        { 
            if (oldy == tabY[i]) 
            { 
                System.arraycopy(desBuf, dy - desW, desBuf, dy, desW); 
            } 
            else 
            { 
                dx = 0; 
                for (int j = 0; j < desW; j++) 
                { 
                    desBuf[dy + dx] = srcBuf[sy + tabX[j]]; 
                    dx++; 
                } 
                sy += (tabY[i] - oldy) * srcW; 
            } 
            oldy = tabY[i]; 
            dy += desW; 
        } 
        // 生成图片 
        desImg = Bitmap.createBitmap(desBuf, desW, desH, Bitmap.Config.ARGB_8888); 
         
        return desImg; 
    } 



常用的Android版缩放图片代码:

view plaincopy to clipboardprint?

    ContentResolver cr = this.getContentResolver(); 
    try 
    { 
        InputStream in = cr.openInputStream(uri); 
        Bitmap bitmap = BitmapFactory.decodeStream(in); 
        try 
        { 
            in.close(); 
        } 
        catch (IOException e) 
        { 
            e.printStackTrace(); 
        } 
        if(null  == bitmap) 
        { 
            Toast.makeText(this, "Head is not set successful,Decode bitmap failure", 2000); 
        } 
        //原始图片的尺寸 
        int bmpWidth  = bitmap.getWidth(); 
        int bmpHeight = bitmap.getHeight(); 
         
        //缩放图片的尺寸 
        float scaleWidth  = (float) 40 / bmpWidth; 
        float scaleHeight = (float) 40 / bmpHeight; 
        Matrix matrix = new Matrix(); 
        matrix.postScale(scaleWidth, scaleHeight); 
         
        //产生缩放后的Bitmap对象 
        Bitmap resizeBitmap = Bitmap.createBitmap( 
            bitmap, 0, 0, bmpWidth, bmpHeight, matrix, false); 
        bitmap.recycle(); 
        //Bitmap to byte[] 
        byte[] photoData = Bitmap2Bytes(resizeBitmap); 
         
        //save file 
        String fileName = "/sdcard/test.jpg"; 
        FileUtil.writeToFile(fileName, photoData); 
         
        //save photo check sum to db 
        DataCenter.GetInstance().ModifyIMMUser(); 
        //refresh ImageView 
    } 
    catch (FileNotFoundException exp) 
    { 
        exp.printStackTrace(); 
    } 



如果图片非常大,在执行BitmapFactory.decodeStream的时候就会抛出OOM异常。

我们来看看系统应用MMS是如何处理的,SMS添加了多媒体附件后就作MMS处理了,当附加文件原图超过300K,也会做个缩放处理,具体参考:com.android.mms.ui/.UriImage:

view plaincopy to clipboardprint?

    package com.android.mms.ui; 
    public class UriImage 
    { 
        private int mWidth; 
        private int mHeight; 
        ... ... 
        // 
        private void decodeBoundsInfo() 
        { 
            InputStream input = null; 
            try 
            { 
                input = mContext.getContentResolver().openInputStream(mUri); 
                BitmapFactory.Options opt = new BitmapFactory.Options(); 
                opt.inJustDecodeBounds = true;//只描边,不读取数据 
                BitmapFactory.decodeStream(input, null, opt); 
                mWidth = opt.outWidth; 
                mHeight = opt.outHeight; 
            } 
            catch (FileNotFoundException e) 
            { 
                // Ignore 
                Log.e(TAG, "IOException caught while opening stream", e); 
            } 
            finally 
            { 
                if (null != input) { 
                    try { 
                        input.close(); 
                    } catch (IOException e) { 
                        // Ignore 
                        Log.e(TAG, "IOException caught while closing stream", e); 
                    } 
                } 
            } 
        } 
        private byte[] getResizedImageData(int widthLimit, int heightLimit) 
        { 
            int outWidth = mWidth; 
            int outHeight = mHeight; 
            int s = 1; 
            while ((outWidth / s > widthLimit) || (outHeight / s > heightLimit)) 
            { 
                s *= 2; 
            } 
            //先设置选项 
            BitmapFactory.Options options = new BitmapFactory.Options(); 
            //returning a smaller image to save memory. 
            options.inSampleSize = s; 
            InputStream input = null; 
            try 
            { 
                input = mContext.getContentResolver().openInputStream(mUri); 
                Bitmap b = BitmapFactory.decodeStream(input, null, options);//注意看options的用法 
                if (b == null) { 
                    return null; 
                } 
                ByteArrayOutputStream os = new ByteArrayOutputStream(); 
                b.compress(CompressFormat.JPEG, MessageUtils.IMAGE_COMPRESSION_QUALITY, os); 
                return os.toByteArray(); 
            } catch (FileNotFoundException e) { 
                Log.e(TAG, e.getMessage(), e); 
                return null; 
            } finally { 
                if (input != null) { 
                    try { 
                        input.close(); 
                    } catch (IOException e) { 
                        Log.e(TAG, e.getMessage(), e); 
                    } 
                } 
            } 
        } 
        ... ... 
    } 



可以看出,MMS应用的方法是:先设置缩放选项,再读取缩放的图片数据到内存,规避了内存引起的OOM。

修改后的代码:

view plaincopy to clipboardprint?

        ContentResolver cr = this.getContentResolver(); 
        try 
        { 
            InputStream in = cr.openInputStream(uri); 
               BitmapFactory.Options options = new BitmapFactory.Options(); 
               options.inJustDecodeBounds = true; 
               BitmapFactory.decodeStream(in, null, options); 
            try 
            { 
        in.close(); 
    } 
            catch (IOException e) 
            { 
        e.printStackTrace(); 
    } 
               int mWidth = options.outWidth; 
               int mHeight = options.outHeight; 
                
               int sWidth  = 40; 
               int sHeight = 40; 
                
            int s = 1; 
            while ((mWidth / s > sWidth * 2) || (mHeight / s > sHeight * 2)) 
            { 
                s *= 2; 
            } 
               options = new BitmapFactory.Options(); 
            options.inSampleSize = s; 
            in = cr.openInputStream(uri); 
            Bitmap bitmap = BitmapFactory.decodeStream(in, null, options); 
            try 
            { 
        in.close(); 
    } 
            catch (IOException e) 
            { 
        e.printStackTrace(); 
    } 
            if(null  == bitmap) 
            { 
                Toast.makeText(this, "Head is not set successful,Decode bitmap failure", 2000); 
                return ; 
            } 
            //原始图片的尺寸 
            int bmpWidth  = bitmap.getWidth(); 
            int bmpHeight = bitmap.getHeight(); 
             
            //缩放图片的尺寸 
            float scaleWidth  = (float) sWidth / bmpWidth; 
            float scaleHeight = (float) sHeight / bmpHeight; 
            Matrix matrix = new Matrix(); 
            matrix.postScale(scaleWidth, scaleHeight); 
             
            //产生缩放后的Bitmap对象 
            Bitmap resizeBitmap = Bitmap.createBitmap( 
                bitmap, 0, 0, bmpWidth, bmpHeight, matrix, false); 
            bitmap.recycle(); 
                    Bitmap resizeBitmap = bitmap; 
            //Bitmap to byte[] 
            byte[] photoData = bitmap2Bytes(resizeBitmap); 
             
            //save file 
            String fileName = "/sdcard/test.jpg"; 
            FileUtil.writeToFile(fileName, photoData); 



view plaincopy to clipboardprint?

    private byte[] bitmap2Bytes(Bitmap bm) 
    { 
        ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
        bm.compress(Bitmap.CompressFormat.JPEG, 100, baos); 
        return baos.toByteArray(); 
    } 



OVER!
1 楼 hongmeikaile 2011-04-02  
我是刚学android的,不懂的地方,还得请教你一下,借鉴了
2 楼 Jclick 2011-04-06  
hongmeikaile 写道
我是刚学android的,不懂的地方,还得请教你一下,借鉴了

我们都一样~~
  相关解决方案