前言
出来混,迟早是要还的。前段时间项目多,时间紧,博客就搁置。有时候自己很奇怪,做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流操作。