android通过相册获取图片oom的问题

前言

出来混,迟早是要还的。前段时间项目多,时间紧,博客就搁置。有时候自己很奇怪,做android开发也有几个年头了。为什么还在写这些基础的东西,为什么不去研究当前很热的技术?首先可能是以前自己给自己留了很多坑,其次,在叼在复杂的框架其根本原理还是离不开基础的,就像程序无非是数据+算法。当然新的技术也肯定是不能落下的。

发现问题

一般app都会有上传图片业务,而上传图像的途径无非是从相册选取,或直接拍照。然而选取或拍照的图片尺寸过大,是很容易造成内存溢出的,即使没有溢出,也需要优化,当然是内存占用越小越好。官方文档推荐的是尽可能不超过16m。

Mobile devices typically have constrained system resources. Android devices can have as little as 16MB of memory available to a single application.

当然近几年手机硬件发展快,国内的手机厂商分配的内存可能会有所增加,但是app的业务量和内存占用也相对增加。所以节省内存成为app优化的关键。关于图片上传的优化例子大多千篇一律,含糊不清。因此,所以就有了这篇帖子。

解决问题

  • 相册
    跳转的代码:

      Intent albumIntent = new Intent(Intent.ACTION_PICK);
      albumIntent.setType("image/*");
      startActivityForResult(albumIntent, REQUEST_IMAGE_PICK);

    接收响应代码:

     private static final int REQUEST_IMAGE_PICK = 1;
    
     @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode == Activity.RESULT_OK) {
                switch (requestCode) {
                    case REQUEST_IMAGE_PICK:
                        if (data != null) {
                            Uri uri = data.getData();
                            if (uri != null) {
                                setPic(mHeadImageIv, uri);
                            }
                        }
                        break;
                }
            }
        }
      private void setPic(ImageView imageView, Uri uri) {
            if (getActivity() != null) {
                //获取目标控件的大小
                int targetW = imageView.getWidth();
                int targetH = imageView.getHeight();
    
                BitmapFactory.Options bmOptions = new BitmapFactory.Options();
                try {
                 //inJustDecodeBounds为true,可以加载源图片的尺寸大小,decodeStream方法返回的bitmap为null
                    bmOptions.inJustDecodeBounds = true;                BitmapFactory.decodeStream(getActivity().getContentResolver().openInputStream(uri), null, bmOptions);
                    // 得到源图片的尺寸
                    int photoW = bmOptions.outWidth;
                    int photoH = bmOptions.outHeight;
                    //通过比较获取较小的缩放比列
                    int scaleFactor = Math.min(photoW / targetW, photoH / targetH);
                    // 将inJustDecodeBounds置为false,设置bitmap的缩放比列
                    bmOptions.inJustDecodeBounds = false;
                    bmOptions.inSampleSize = scaleFactor;
                    bmOptions.inPurgeable = true;
                    //再次decode获取bitmap
                    Bitmap bitmap = BitmapFactory.decodeStream(getActivity().getContentResolver().openInputStream(uri), null, bmOptions);
                    imageView.setImageBitmap(bitmap);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }

    亲测通过上述方法,bitmap的大小为225k,而通过下面的方法获取的bitmap大小为3.2m。所以内存是很大的节省。

     if (data != null) {
        Uri uri = data.getData();
        if (uri != null) {
           Bitmap bitmap = null;
           try {
            bitmap = (Bitmap) MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), uri);
           if (bitmap != null) {
                 mHeadImageIv.setImageBitmap(bitmap);
               }
            } catch (IOException e) {
                e.printStackTrace();
              }
    
         }
    }

    注意如果需要上传图片,那么就需要获取到相册图片的绝对路径,可以参考:
    http://www.jianshu.com/p/b168cbe50066

  • 拍照

    权限

      <uses-feature android:name="android.hardware.camera"
                      android:required="true" />

    调用相机代码

    mPhotoFile = new File(getPhotoPath(), getPhotoName());
                            if (!mPhotoFile.getParentFile().exists()) {
                                mPhotoFile.getParentFile().mkdirs();
                            }
                            Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mPhotoFile));
                            if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
                                startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
                            }

    接受相机返回代码

     private static final int REQUEST_IMAGE_CAPTURE= 2;
    
     @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode == Activity.RESULT_OK) {
                switch (requestCode) {
                    case REQUEST_IMAGE_CAPTURE:
                        String path = mPhotoFile.getAbsolutePath();
                        Uri uri = Uri.parse("file://" + path);
                        setPic(mFrontImageIv, uri);
                        break;
                }
            }
        }

    可以看出拍照返回的bitmap的尺寸可能也很大,所以也可以通过BitmapFactory.Options来优化。

在Android2.3中,默认的Bitmap为32位,类型是ARGB_8888,也就意味着一个像素点占用4个字节的内存。所以一个bitmap的内存计算为:3200*2400*4 bytes = 30M

总结

其实,关于bitmap的操作,只要注意在不能确定bitmap的尺寸的时候别轻易的实例化一个bitmap,用完bitmap及时回收。图片就让它安静的呆在存储上,就算是需要显示也显示BitmapFactory.Option优化过的图片。上传后台的直接走Io流操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值