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 }
复制代码

  效果展示:

 

  源码下载

 

总结

  其实对于这两个简单的Demo而言,只需要针对分辨率进行优化即可,一般而言因为功能简单,系统配置只要还过的去,都是可以被正常GC的,但是对于一些经常操作图片的应用来说,还是显式的通过代码的方式来管理Bitmap的内存。最后加入Canvas进行渲染水印,不是必须的,只是加了个功能而已,直接使用bitmap对象也可以。



本文转自承香墨影博客园博客,原文链接:http://www.cnblogs.com/plokmju/p/android_BitmapRecycle.html,如需转载请自行联系原作者


相关文章
|
2月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
239 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
3月前
|
存储 消息中间件 人工智能
【08】AI辅助编程完整的安卓二次商业实战-修改消息聊天框背景色-触发聊天让程序异常终止bug牵涉更多聊天消息发送优化处理-优雅草卓伊凡
【08】AI辅助编程完整的安卓二次商业实战-修改消息聊天框背景色-触发聊天让程序异常终止bug牵涉更多聊天消息发送优化处理-优雅草卓伊凡
253 10
【08】AI辅助编程完整的安卓二次商业实战-修改消息聊天框背景色-触发聊天让程序异常终止bug牵涉更多聊天消息发送优化处理-优雅草卓伊凡
|
3月前
|
Linux 测试技术 语音技术
【车载Android】模拟Android系统的高负载环境
本文介绍如何将Linux压力测试工具Stress移植到Android系统,用于模拟高负载环境下的CPU、内存、IO和磁盘压力,帮助开发者优化车载Android应用在多任务并发时的性能问题,提升系统稳定性与用户体验。
239 6
|
3月前
|
Java 数据库 Android开发
基于Android的电子记账本系统
本项目研究开发一款基于Java与Android平台的开源电子记账系统,采用SQLite数据库和Gradle工具,实现高效、安全、便捷的个人财务管理,顺应数字化转型趋势。
|
7月前
|
XML Android开发 数据格式
Android利用selector(选择器)实现图片动态点击效果
本文介绍了Android中ImageView的`src`与`background`属性的区别及应用,重点讲解如何通过设置背景选择器实现图片点击动态效果。`src`用于显示原图大小,不拉伸;`background`可随组件尺寸拉伸。通过创建`selector_setting.xml`,结合`setting_press.xml`和`setting_normal.xml`定义按下和正常状态的背景样式,提升用户体验。示例代码展示了具体实现步骤,包括XML配置和形状定义。
311 3
Android利用selector(选择器)实现图片动态点击效果
|
7月前
|
Java Android开发
Android图片的手动放大缩小
本文介绍了通过缩放因子实现图片放大缩小的功能,效果如动图所示。关键步骤包括:1) 在布局文件中设置 `android:scaleType="matrix"`;2) 实例化控件并用 `ScaleGestureDetector` 处理缩放手势;3) 使用 `Matrix` 对图片进行缩放处理。为避免内存崩溃,可在全局配置添加 `android:largeHeap="true"`。代码中定义了 `beforeScale` 和 `nowScale` 变量控制缩放范围,确保流畅体验。
238 8
|
7月前
|
缓存 编解码 Android开发
Android内存优化之图片优化
本文主要探讨Android开发中的图片优化问题,包括图片优化的重要性、OOM错误的成因及解决方法、Android支持的图片格式及其特点。同时介绍了图片储存优化的三种方式:尺寸优化、质量压缩和内存重用,并详细讲解了相关的实现方法与属性。此外,还分析了图片加载优化策略,如异步加载、缓存机制、懒加载等,并结合多级缓存流程提升性能。最后对比了几大主流图片加载框架(Universal ImageLoader、Picasso、Glide、Fresco)的特点与适用场景,重点推荐Fresco在处理大图、动图时的优异表现。这些内容为开发者提供了全面的图片优化解决方案。
283 1
|
8月前
|
安全 搜索推荐 Android开发
Android系统SELinux安全机制详解
如此看来,SELinux对于大家来说,就像那位不眠不休,严阵以待的港口管理员,守护我们安卓系统的平安,维护这片海港的和谐生态。SELinux就这样,默默无闻,却卫士如山,给予Android系统一份厚重的安全保障。
268 18
|
Android开发 数据格式 XML
Android异步加载图片详解之方式二(3)
main.xml如下:   listviewitem.xml如下:   ...
863 0
|
缓存 Java Android开发
Android异步加载图片详解之方式二(2)
FileCache.java如下: package com.cn.loadImages; import java.io.File; import java.
957 0

热门文章

最新文章