Android--从系统Camera和Gallery获取图片优化
承香墨影
Android--从系统Camera和Gallery获取图片优化
前言
之前有两篇博客讲解了如何从系统内已有的Camera和Gallery应用中获取图片的例子,看到评论里有朋友说有时候会报错,导致程序崩溃的问题。本篇博客主要就这个问题分析讲解一下,最后将以一个简单的Demo演示。关于从系统内已有的Camera和Gallery应用中获取图片还不了解的朋友,可以先看看另外两篇博客:Android--调用系统照相机拍照与摄像、Android--从系统Gallery获取图片。
分析出错原因
之前讲到的从系统现有的Camera和Gallery应用中获取图片的Demo中,均直接使用系统应用返回的Uri,通过ImageView.setImageURI(Uri)方法显示在界面上。而对于Android设备来说,向内存中加载一张图片,消耗的内存并不受图片的大小而影响,影响它的是图片的分辨率,图片的分辨率越大加载到内存所占用的内存将越多。使用ImageView.setImageURI(Uri)方法将导致了一个严重的错误,虽然ImageView直接引用图片的Uri,它会对图片进行一部分优化,使得它可以正常显示,但是这种办法不利于Bitmap资源的回收。所以在重复操作之后,经历过多次的GC,也没有办法回收出足够加载图片的内存,导致应用崩溃。
解决方案
既然已经知道导致程序崩溃的原因是内存溢出导致的,那么只需要维护好Uri所代表的图片内存即可。具体优化流程如下:
1、系统中现有的Camera和Gallery应用获取图片返回的都是一个Uri类型的数据,它是一个内容提供者的路径,可以使用ContentResolver获取它,这个以前有讲过,不了解的朋友可以看看另外一篇博客:Android--ContentProvider。而在Context中,可以使用getContentResolver()方法获取到当前的内容解析者,并通过它的openInputStream()方法获取到图片的输入流,通过输入流可以获取到一个Bitmap对象。
2、上面提到,Android中加载图片到内存中所占内存的大小取决于图片的分辨率,所有得到Bitmap还不能直接使用它,必须对其进行优化,以最大适应当前设备的屏幕分辩率又不会导致加载过多像素而导致内存不足的情况。关于加载大分辨率到内存还不了解的朋友可以参见另外一篇博客:Android--加载大分辨率图片到内存。
3、得到了优化过后的图片还需要在使用过后进行回收,Bitmap提供了两个方法用于判断是否已经回收它以及强制Bitmap回收自己。以下是它们的完整签名:
boolean isRecycled():返回Bitmap对象是否已经被回收。
void recycle():强制一个Bitmap对象回收自己。
优化后的Demo
上面讲到的两个demo,从Gallery中获取图片比较简单,代码量小,那么就在这个基础之上进行代码的优化。从Gallery中获取图片的Uri并不直接使用,而是把它转化为一个Bitmap,并且优化它以达到适应屏幕分辨率的效果。
复制代码
1 package cn.bgxt.sysgallerydemo;
2
3 import java.io.InputStream;
4
5 import android.net.Uri;
6 import android.os.Bundle;
7 import android.util.Log;
8 import android.view.View;
9 import android.view.WindowManager;
10 import android.view.View.OnClickListener;
11 import android.widget.Button;
12 import android.widget.ImageView;
13 import android.widget.Toast;
14 import android.app.Activity;
15 import android.content.Intent;
16 import android.graphics.Bitmap;
17 import android.graphics.BitmapFactory;
18 import android.graphics.Canvas;
19 import android.graphics.Color;
20 import android.graphics.BitmapFactory.Options;
21 import android.graphics.Matrix;
22 import android.graphics.Paint;
23
24 public class MainActivity extends Activity {
25 private Button btn_getImage;
26 private ImageView iv_image;
27 private final static String TAG = "main";
28 private WindowManager wm;
29 private Bitmap bitmap;
30 private Bitmap blankBitmap;
31
32 @Override
33 protected void onCreate(Bundle savedInstanceState) {
34 super.onCreate(savedInstanceState);
35 setContentView(R.layout.activity_main);
36
37 // 得到应用窗口管理器
38 wm = getWindowManager();
39 btn_getImage = (Button) findViewById(R.id.btn_getImage);
40 iv_image = (ImageView) findViewById(R.id.iv_image);
41
42 btn_getImage.setOnClickListener(getImage);
43
44 }
45
46 private View.OnClickListener getImage = new OnClickListener() {
47
48 @Override
49 public void onClick(View v) {
50 // 设定action和miniType
51 Intent intent = new Intent();
52 intent.setAction(Intent.ACTION_PICK);
53 intent.setType("image/*");
54 // 以需要返回值的模式开启一个Activity
55 startActivityForResult(intent, 0);
56 }
57 };
58
59 @Override
60 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
61 // 如果获取成功,resultCode为-1
62 Log.i(TAG, "resultCode:" + resultCode);
63 if (requestCode == 0 && resultCode == -1) {
64 // 获取原图的Uri,它是一个内容提供者的地址
65 Uri uri = data.getData();
66 Log.i(TAG, "uri:" + data.getData().toString());
67 try {
68 // 从ContentResolver中获取到Uri的输入流
69 InputStream is = getContentResolver().openInputStream(uri);
70
71 // 得到屏幕的宽和高
72 int windowWidth = wm.getDefaultDisplay().getWidth();
73 int windowHeight = wm.getDefaultDisplay().getHeight();
74
75 // 实例化一个Options对象
76 BitmapFactory.Options opts = new BitmapFactory.Options();
77 // 指定它只读取图片的信息而不加载整个图片
78 opts.inJustDecodeBounds = true;
79 // 通过这个Options对象,从输入流中读取图片的信息
80 BitmapFactory.decodeStream(is, null, opts);
81
82 // 得到Uri地址的图片的宽和高
83 int bitmapWidth = opts.outWidth;
84 int bitmapHeight = opts.outHeight;
85 // 分析图片的宽高比,用于进行优化
86 if (bitmapHeight > windowHeight || bitmapWidth > windowWidth) {
87 int scaleX = bitmapWidth / windowWidth;
88 int scaleY = bitmapHeight / windowHeight;
89 if (scaleX > scaleY) {
90 opts.inSampleSize = scaleX;
91 } else {
92 opts.inSampleSize = scaleY;
93 }
94 } else {
95 opts.inSampleSize = 1;
96 }
97
98 // 设定读取完整的图片信息
99 opts.inJustDecodeBounds = false;
100 is = getContentResolver().openInputStream(uri);
101
102 // 如果没有被系统回收,就强制回收它
103 if (blankBitmap != null && !bitmap.isRecycled()) {
104 bitmap.recycle();
105 }
106 bitmap = BitmapFactory.decodeStream(is, null, opts);
107
108 // 如果没有被系统回收,就强制回收它
109 if (blankBitmap != null && !blankBitmap.isRecycled()) {
110 blankBitmap.recycle();
111 }
112 // 在内存中创建一个可以操作的Bitmap对象
113 blankBitmap = Bitmap.createBitmap(bitmap.getWidth(),
114 bitmap.getHeight(), Bitmap.Config.ARGB_8888);
115 // 为图片添加一个画板
116 Canvas canvas = new Canvas(blankBitmap);
117 // 把读取的图片画到新创建的Bitmap对象中
118 canvas.drawBitmap(bitmap, new Matrix(), new Paint());
119 Paint paint = new Paint();
120 paint.setColor(Color.RED);
121 paint.setTextSize(30);
122 // 通过创建的画笔,在Bitmap上写入水印
123 canvas.drawText("我是水印", 10, 50, paint);
124
125 iv_image.setImageBitmap(blankBitmap);
126 } catch (Exception e) {
127 Toast.makeText(MainActivity.this, "获取图片失败", 0).show();
128 }
129 }
130 super.onActivityResult(requestCode, resultCode, data);
131 }
132 }
报名学习加微信/QQ 1602007,关注《东方联盟网》微信公众号
>更多相关文章
首页推荐
佛山市东联科技有限公司一直秉承“一切以用户价值为依归
- 01-11全球最受赞誉公司揭晓:苹果连续九年第一
- 12-09罗伯特·莫里斯:让黑客真正变黑
- 12-09谁闯入了中国网络?揭秘美国绝密黑客小组TA
- 12-09警示:iOS6 惊现“闪退”BUG
- 03-08消息称微软开发内部AI推理模型,或将成为Op
- 03-08美国法院驳回马斯克请求,未阻止OpenAI转型
- 03-08饿了么成立即时配送算法专家委员会 持续全局
- 03-08长安汽车:预计今年底长安飞行汽车将完成试
- 03-08谷歌推出虚拟试穿、AR美妆新功能
相关文章
24小时热门资讯
热门推荐
最新资讯
操作系统
黑客防御