在上文提到的方法中,实际上我们对于4.4以上的手机是避开了大图问题的,也就是对于大图(或者高清手机拍摄的照片)采用上文的方法是有问题的,特别是小米,小米就是个坑!!!选取的照片剪切时必须将剪切框选择再小再小,所以更本就不能实现对大图的裁剪,因为我们设置了
intent.putExtra("return-data",true);
也就是说返回的是bitmap图,很占用内存,所以必须考虑采用本文的方法,对裁剪的图片存uri,即:
intent.putExtra("return-data",false);
intent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
但是对于上述uri方法,在某些奇葩的机器上又会出现奇葩的问题,4.4以上是拿不到uri数据的,小米的又可以,LG的某款机不行。所以,笨人的方法就是采用返回数据,也就是上文采用的方法,把大图直接给忽略掉了(不喜欢小米,就忽略小米咯)。。。话说这样是不行的,于是就有了此篇文章......对于上文的方法实际也可以直接屏蔽掉返回数据,全部设置为false,返回uri这样就是本文要讲到的方法。
在Android中,Intent触发Camera程序,拍好照片后,将会返回数据,但是考虑到内存问题,Camera不会将全尺寸的图像返回给调用的Activity,一般情况下,有可能返回的是缩略图,比如120*160px。
这是为什么呢?这不是一个Bug,而是经过精心设计的,却对开发者不透明。
以小米手机为例,摄像头800W像素,根据我目前设置拍出来的图片尺寸为3200*2400px。有人说,那就返回呗,大不了耗1-2M的内存,不错,这个尺寸的图片确实只有1.8M左右的大小。但是你想不到的是,这个尺寸对应的Bitmap会耗光你应用程序的所有内存。Android出于安全性考虑,只会给你一个寒碜的缩略图。
在Android2.3中,默认的Bitmap为32位,类型是ARGB_8888,也就意味着一个像素点占用4个字节的内存。我们来做一个简单的计算题:3200*2400*4bytes = 30M。
如此惊人的数字!哪怕你愿意为一张生命周期超不过10s的位图愿意耗费这么巨大的内存,Android也不会答应的。
Mobiledevices typically have constrained system resources.
Androiddevices can have as little as 16MB of memory available to asingle application.
参考博客在于解决了Android对返回图片的大小限制,并且详细解释了裁剪图片的Intent附加数据的具体含义。
具体参数:
Intent("com.android.camera.action.CROP")对应的所有可选数据都一目了然。在了解上面个个选项的含义之后,最令人困惑的是MediaStore.EXTRA_OUTPUT以及return-data选项。我们将目光着眼于三个极为重要的选项:
data、MediaStore.EXTRA_OUTPUT以及return-data。
data和MediaStore.EXTRA_OUTPUT都是可选的传入数据选项,你可以选择设置data为Bitmap,或者将相应的数据与URI关联起来,你也可以选择是否返回数据(return-data:true)。即,有两种方式从这个Intent中取得返回的bitmap:获取内部数据或者提供一个Uri以便程序可以将数据写入
为什么还有不用返回数据的选项?如果对URI足够了解的话,应该知道URI与File相似,你所有的操作如裁剪将数据都保存在了URI中,你已经持有了相应的URI,也就无需多此一举,再返回Bitmap了。
前面已经说到,可以设置data为Bitmap,但是这种操作的限制在于,你的Bitmap不能太大。因此,我们前进的思路似乎明确了:截大图用URI,小图用Bitmap。思路如下:
上面说道的两种方法:
方法1:如果你将return-data设置为“true”,你将会获得一个与内部数据关联的Action,并且bitmap以此方式返回:(Bitmap)extras.getParcelable("data")。注意:如果你最终要获取的图片非常大,那么此方法会给你带来麻烦,所以你要控制outputX和outputY保持在较小的尺寸。鉴于此原因,在我的代码中没有使用此方法((Bitmap)extras.getParcelable("data"))。
下面是CropImage.java的源码片段:
//Return the cropped image directly or save it to the specified URI.
BundlemyExtras = getIntent().getExtras();
if (myExtras!= null &&(myExtras.getParcelable("data")!= null||myExtras.getBoolean("return-data")))
{
Bundleextras = new Bundle();
extras.putParcelable("data",croppedImage);
setResult(RESULT_OK,(new Intent()).setAction("inline-data").putExtras(extras));
finish();
}
方法2:如果你将return-data设置为“false”,那么在onActivityResult的Intent数据中你将不会接收到任何Bitmap,相反,你需要将
MediaStore.EXTRA_OUTPUT关联到一个Uri,此Uri是用来存放Bitmap的。
但是还有一些条件,首先你需要有一个短暂的与此Uri相关联的文件地址,当然这不是个大问题(除非是那些没有sdcard的设备)。
下面是CropImage.java关于操作Uri的源码片段:
if(mSaveUri != null)
{
OutputStream outputStream = null;
try{
outputStream = mContentResolver.openOutputStream(mSaveUri);
if(outputStream != null){
croppedImage.compress(mOutputFormat, 75, outputStream);
}
} catch(IOException ex) {
//TODO:report error to caller
Log.e(TAG,"Cannotopen file: "+ mSaveUri, ex);
} finally{
Util.closeSilently(outputStream);
}
Bundle extras = newBundle();
setResult(RESULT_OK,newIntent(mSaveUri.toString()).putExtras(extras));
}