天天看點

Android 中解決圖檔加載OOM 之 bitmap 壓縮顯示,壓縮上傳

java.lang.OutOfMemoryError

OOM就是記憶體溢出,即Out Of Memory。也就是說記憶體占有量超過了VM所配置設定的最大。

現在的手機照相功能強大圖檔的大小往往達到好幾兆,并且。android中處理圖檔的基礎類是Bitmap,顧名思義,就是位圖。占用記憶體的算法如:圖檔的width*height*Config。 如果Config設定為ARGB_8888,那麼上面的Config就是4。一張480*320的圖檔占用的記憶體就是480*320*4  byte。 在預設情況下android程序的記憶體占用量為16M,是以導緻了記憶體溢出,

在手機應用中 如果單獨将一張圖檔加載顯示,比如說微信發送圖檔時可以預覽圖檔,這種情況下直接加載圖檔也就是用BItMap

 BitmapFactory.decodeFile(pathName);不進行壓縮一般不會出現記憶體溢出,但是在多張圖檔展示時,會出現oom導緻程式崩潰這時我們要進行圖檔壓縮,這部分代碼大家都應該很熟悉了,

public Bitmap revitionImageSize(String path) throws IOException {
        BufferedInputStream in = null;
        try {
           
<span style="white-space:pre">		</span>//根據位址轉換為 檔案輸入流對象 也可以把File轉換成輸入對象 
            in = new BufferedInputStream(new FileInputStream(path));
            BitmapFactory.Options options = new BitmapFactory.Options();
           
//<span style="font-family: 'Microsoft YaHei', Verdana, sans-serif, 宋體;">Options 設定圖檔屬性</span>
            options.inJustDecodeBounds = true;//這個屬性是重點這樣設定之後
            BitmapFactory.decodeStream(in, null, options);// 調用這個方法可以不把圖檔加載到記憶體中,但可以擷取圖檔的寬高大小
            in.close();
            in = null;
            int i = 0;
            Bitmap bitmap = null;
            while (true) {
                if ((options.outWidth >> i <= 256)
                        && (options.outHeight >> i <= 256)) {
                    in = new BufferedInputStream(new FileInputStream(new File(
                            path)));
                   //調整圖檔大小
                    options.inSampleSize = calculateInSampleSize(options,
                            CommonUtil.dip2px(this, 80),
                            CommonUtil.dip2px(this, 80));
                    options.inJustDecodeBounds = false;//設定加載到記憶體中
                    bitmap = BitmapFactory.decodeStream(in, null, options); //建立圖檔
                    break;
                }
                i += 1;
            }
            return bitmap;
        } catch (Exception e) {
            Log.e("System.out", e.getMessage());
            return null;
        } finally {
            if (null != in) {//關閉流
                in.close();
                in = null;
            }
        }
    }

    // 按照寬高中 短的壓縮 
    public int calculateInSampleSize(BitmapFactory.Options options, 
            int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight; 
        final int width = options.outWidth; 
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {//判斷實際大小是不是不要求大小 大
            if (width > height) { //如果 寬度筆高地長的話 安短的一邊 計算壓縮比
                inSampleSize = Math.round((float) height / (float) reqHeight);
            } else {
                inSampleSize = Math.round((float) width / (float) reqWidth);
            }
        }
        return inSampleSize;
    }
           

這樣傳回壓縮好的圖檔,這樣隻是單純的壓縮了一張圖檔 顯示一張圖檔,做圖檔相冊類的activity時還涉及到周遊手機sd卡裡的所有圖檔這裡就不到說了,

壓縮圖檔上傳

圖檔壓縮的部分跟

上面也是差不多的

private void setUpImageFile() {
        FileInputStream is = null;

        File f = null, imageFile; //f壓縮後的圖檔 壓縮前的圖檔檔案
        Bitmap image = null;
        try {
            imageFile = new File(path);
            if (imageFile.length() <= 800 * 1024) { // 判端檔案大小 800k以内直接上傳
                this.upPath = path;
                return;
            }
            is = new FileInputStream(imageFile);
            // 定義一個file,為壓縮後的圖檔
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:sss");
           
//建立個檔案
            f = new File(Environment.getExternalStorageDirectory() + “/image”, simpleDateFormat.format(new Date()) + "上傳.jpeg");
            if (!f.getParentFile().exists())//建立目錄
                f.getParentFile().mkdirs();
            int size = (int) (imageFile.length() / (1024 * 1024) * 10);// 計算目标檔案的大小 大于1024K壓縮到1024k以内 
            size = (int) Math.rint(size / 10);
            Options options = new Options();
            options.inSampleSize = size;
            // 将圖檔縮小為原來的 1/size ,上傳
            image = BitmapFactory.decodeStream(is, null, options);
            is.close();
        } catch (Exception e1) {
            e1.printStackTrace();
            return;
        }
       // 把bitmap建立處理 再次壓縮
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 這裡100表示不壓縮,将不壓縮的資料存放到baos中
        int per = 100;
        while (baos.toByteArray().length / 1024 > 1024) { // 循環判斷如果壓縮後圖檔是否大于1024kb,大于繼續壓縮
            baos.reset();// 重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, per, baos);// 将圖檔壓縮為原來的(100-per)%,把壓縮後的資料存放到baos中
            per -= 5;// 每次都減少10

        }
        // 回收圖檔,清理記憶體
        if (image != null && !image.isRecycled()) {
            image.recycle();
            image = null;
            System.gc();
        }
           
//把記憶體中的 image 寫到檔案中
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把壓縮後的資料baos存放到ByteArrayInputStream中
        try {
            baos.close();
            FileOutputStream os;
            os = new FileOutputStream(f);
            // 将輸入流複制到輸出流中
            CopyStream(isBm, os);
            isBm.close();
            os.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        this.upPath = f.getAbsolutePath();
    }

    private void CopyStream(ByteArrayInputStream isBm, FileOutputStream os) {
        byte[] buff = new byte[1024];
        int length = 0;
        try {
            while ((length = isBm.read(buff)) != -1) {
                os.write(buff, 0, length);
            }
        } catch (IOException e) { 
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
           

這樣在sd的/image目錄下建立了等待上傳的壓縮後的檔案