android调用相机拍照后,导致OOM

背景:调用相机拍照,结果返回时,导致内存迅速升高,内存不足时会导致OOM

发现的原因:在相机拍照后,会生成很大的照片,分辨率也很高,因此占用的内存会很大,在网上找了一些同学的相机拍照代码块发现,使用到一个BitmapHelper的类,本类的主要作用是:分析拍摄的照片,如果有方向不对了对照片进行旋转,进行旋转需要吧图片加载到内存中,原尺寸的照片通常会很大,会占用两个Bitmap才能够完成旋转,因此bitmap helper在旋转之前进行了尺寸的压缩。
不同的手机或者pad对尺寸进行修改会造成所放的比例计算差异。。。好多废话,下面是我对Bitmap Helper类的简单修改,能够让不同的尺寸或方向的照片进行正确的缩放。

Bitmap Helper能够将拍摄的照片缩放后保存在某个目录中:

public class BitmapHelper {

    /**
     * get the orientation of the bitmap {@link ExifInterface}
     *
     * @param path
     * @return
     */
    public static int getDegress(String path) {
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface.getAttributeInt(
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }

    /**
     * rotate the bitmap
     *
     * @param bitmap
     * @param degress
     * @return
     */
    public static Bitmap rotateBitmap(Bitmap bitmap, int degress) {
        if (bitmap != null) {
            Matrix m = new Matrix();
            m.postRotate(degress);
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
            return bitmap;
        }
        return bitmap;
    }

    /**
     * caculate the bitmap sampleSize
     *
     * @param
     * @return
     */
    public static int caculateInSampleSize(Options options, int rqsW, int rqsH) {
        int height = options.outHeight;
        int width = options.outWidth;
        //主要修改这里,让最宽的对应设定的宽,高同理
        if (width > height) {
            int temp = width;
            width = height;
            height = temp;
        }
        if (rqsW > rqsH) {
            int rqsT = rqsH;
            rqsH = rqsW;
            rqsW = rqsT;
        }

        int inSampleSize = 1;
        if (rqsW == 0 || rqsH == 0) return 1;
        if (height > rqsH || width > rqsW) {
            int heightRatio = Math.round((float) height / (float) rqsH);
            int widthRatio = Math.round((float) width / (float) rqsW);
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        return inSampleSize;
    }
    /**
     * 根据需要压缩到某尺寸压缩指定路径的图片,并得到图片对象
     *
     * @param path
     * @param rqsW
     * @param rqsH
     * @return
     */
    public static Bitmap compressBitmap(String path, int rqsW, int rqsH) {
        Options options = getBitmapOptions(path);
        options.inSampleSize = caculateInSampleSize(options, rqsW, rqsH);
        return BitmapFactory.decodeFile(path, options);
    }

    /**
     * 压缩指定路径图片,并将其保存在缓存目录中,通过isDelSrc判定是否删除源文件,并获取到缓存后的图片路径
     *
     * @param context
     * @param srcPath
     * @param rqsW
     * @param rqsH
     * @param isDelSrc
     * @return
     */
    public static String compressBitmap(Context context, String srcPath, int rqsW, int rqsH, boolean isDelSrc) {
        int degree = getDegress(srcPath);
        Bitmap bitmap = compressBitmap(srcPath, rqsW, rqsH);//根据长宽以及图片的长宽得到缩放图片
        File srcFile = new File(srcPath);
        String desPath = getImageCacheDir(context) + srcFile.getName();
        try {
            if (degree != 0) bitmap = rotateBitmap(bitmap, degree);
            File file = new File(desPath);
            FileOutputStream fos = new FileOutputStream(file);
            bitmap.compress(CompressFormat.JPEG, 80, fos);//80是图片质量
            fos.close();
            if (isDelSrc) srcFile.deleteOnExit();

        } catch (Exception e) {
        }

        bitmap.recycle();
        System.gc();

        return desPath;
    }

    /**
     * 压缩某个输入流中的图片,可以解决网络输入流压缩问题,并得到图片对象
     *
     * @return Bitmap {@link Bitmap}
     */
    public static Bitmap compressBitmap(InputStream is, int reqsW, int reqsH) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ReadableByteChannel channel = Channels.newChannel(is);
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (channel.read(buffer) != -1) {
                buffer.flip();
                while (buffer.hasRemaining()) baos.write(buffer.get());
                buffer.clear();
            }
            byte[] bts = baos.toByteArray();
            Bitmap bitmap = compressBitmap(bts, reqsW, reqsH);
            is.close();
            channel.close();
            baos.close();
            return bitmap;
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 压缩指定byte[]图片,并得到压缩后的图像
     *
     * @param bts
     * @param reqsW
     * @param reqsH
     * @return
     */
    public static Bitmap compressBitmap(byte[] bts, int reqsW, int reqsH) {
        Options options = new Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(bts, 0, bts.length, options);
        options.inSampleSize = caculateInSampleSize(options, reqsW, reqsH);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeByteArray(bts, 0, bts.length, options);
    }

    /**
     * 压缩已存在的图片对象,并返回压缩后的图片
     *
     * @param bitmap
     * @param reqsW
     * @param reqsH
     * @return
     */
    public static Bitmap compressBitmap(Bitmap bitmap, int reqsW, int reqsH) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitmap.compress(CompressFormat.PNG, 100, baos);
            byte[] bts = baos.toByteArray();
            Bitmap res = compressBitmap(bts, reqsW, reqsH);
            baos.close();
            return res;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return bitmap;
        }
    }

    /**
     * 压缩资源图片,并返回图片对象
     *
     * @param res   {@link Resources}
     * @param resID
     * @param reqsW
     * @param reqsH
     * @return
     */
    public static Bitmap compressBitmap(Resources res, int resID, int reqsW, int reqsH) {
        Options options = new Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resID, options);
        options.inSampleSize = caculateInSampleSize(options, reqsW, reqsH);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resID, options);
    }
    /**
     * 得到指定路径图片的options
     *
     * @param srcPath
     * @return Options {@link Options}
     */
    public static Options getBitmapOptions(String srcPath) {
        Options options = new Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(srcPath, options);
        options.inJustDecodeBounds = false;
        return options;
    }

    /**
     * 获取图片缓存路径
     *
     * @param context
     * @return
     */
private static String getImageCacheDir(Context context) {
        String dir = FileUtils.getCacheDir() + "Image" + File.separator;
        File file = new File(dir);
        if (!file.exists()) file.mkdirs();
        return dir;
    }
}

压缩的方式

上面不仅用到尺寸压缩,而且用到了图片质量压缩
bitmap.compress(CompressFormat.JPEG, 80, fos);//80是图片质量
这个就是质量压缩。
质量压缩需要把文件加载到内存中,因此大图片质量压缩也会瞬间占用大量内存,因此我使用的是先尺寸压缩,再质量压缩的方式。

关于质量压缩

质量压缩android比IOS的质量压缩采用的据说是同一个压缩方式,但是压缩中的一个参数,android默认设置为false,IOS默认设置为true,结果是设置为相同质量压缩下,iOS文件占用的空间比android少。因此我们可以把压缩的源代码搞过来,把false改为true。
我的实际测试,质量的压缩,true的比false仅仅小了30K~100k(图片越小越不明显) 。
使用方式就是:倒入.so文件和NativeUtil

so文件下载地址

public class NativeUtil {
    private static int DEFAULT_QUALITY = 95;

    public static void compressBitmap(Bitmap bit, String fileName,
            boolean optimize) {
        compressBitmap(bit, DEFAULT_QUALITY, fileName, optimize);

    }

    public static void compressBitmap(Bitmap bit, int quality, String fileName,
            boolean optimize) {
        Log.d("native", "compress of native");
        if (bit.getConfig() != Config.ARGB_8888) {
            Bitmap result = Bitmap.createBitmap(bit.getWidth(), bit.getHeight(),
                    Config.ARGB_8888);
            Canvas canvas = new Canvas(result);
            Rect rect = new Rect(0, 0, bit.getWidth(), bit.getHeight());
            canvas.drawBitmap(bit, null, rect, null);
            saveBitmap(result, quality, fileName, optimize);
            result.recycle();
        } else {
            saveBitmap(bit, quality, fileName, optimize);
        }

    }

    private static void saveBitmap(Bitmap bit, int quality, String fileName,
            boolean optimize) {
        compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality,
                fileName.getBytes(), optimize);

    }

    private static native String compressBitmap(Bitmap bit, int w, int h,
            int quality, byte[] fileNameBytes, boolean optimize);

    static {
        System.loadLibrary("jpegbither");
        System.loadLibrary("bitherjni");

    }

}

博主github主页:Github主页 有一个仿IOS的旋转等待View,欢迎使用点星星

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leefeng.top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值