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,并且优化它以达到适应屏幕分辨率的效果。
package cn.bgxt.sysgallerydemo; import java.io.InputStream; import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.BitmapFactory.Options;
import android.graphics.Matrix;
import android.graphics.Paint; public class MainActivity extends Activity {
private Button btn_getImage;
private ImageView iv_image;
private final static String TAG = "main";
private WindowManager wm;
private Bitmap bitmap;
private Bitmap blankBitmap; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 得到应用窗口管理器
wm = getWindowManager();
btn_getImage = (Button) findViewById(R.id.btn_getImage);
iv_image = (ImageView) findViewById(R.id.iv_image); btn_getImage.setOnClickListener(getImage); } private View.OnClickListener getImage = new OnClickListener() { @Override
public void onClick(View v) {
// 设定action和miniType
Intent intent = new Intent();
intent.setAction(Intent.ACTION_PICK);
intent.setType("image/*");
// 以需要返回值的模式开启一个Activity
startActivityForResult(intent, 0);
}
}; @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 如果获取成功,resultCode为-1
Log.i(TAG, "resultCode:" + resultCode);
if (requestCode == 0 && resultCode == -1) {
// 获取原图的Uri,它是一个内容提供者的地址
Uri uri = data.getData();
Log.i(TAG, "uri:" + data.getData().toString());
try {
// 从ContentResolver中获取到Uri的输入流
InputStream is = getContentResolver().openInputStream(uri); // 得到屏幕的宽和高
int windowWidth = wm.getDefaultDisplay().getWidth();
int windowHeight = wm.getDefaultDisplay().getHeight(); // 实例化一个Options对象
BitmapFactory.Options opts = new BitmapFactory.Options();
// 指定它只读取图片的信息而不加载整个图片
opts.inJustDecodeBounds = true;
// 通过这个Options对象,从输入流中读取图片的信息
BitmapFactory.decodeStream(is, null, opts); // 得到Uri地址的图片的宽和高
int bitmapWidth = opts.outWidth;
int bitmapHeight = opts.outHeight;
// 分析图片的宽高比,用于进行优化
if (bitmapHeight > windowHeight || bitmapWidth > windowWidth) {
int scaleX = bitmapWidth / windowWidth;
int scaleY = bitmapHeight / windowHeight;
if (scaleX > scaleY) {
opts.inSampleSize = scaleX;
} else {
opts.inSampleSize = scaleY;
}
} else {
opts.inSampleSize = 1;
} // 设定读取完整的图片信息
opts.inJustDecodeBounds = false;
is = getContentResolver().openInputStream(uri); // 如果没有被系统回收,就强制回收它
if (blankBitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
bitmap = BitmapFactory.decodeStream(is, null, opts); // 如果没有被系统回收,就强制回收它
if (blankBitmap != null && !blankBitmap.isRecycled()) {
blankBitmap.recycle();
}
// 在内存中创建一个可以操作的Bitmap对象
blankBitmap = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
// 为图片添加一个画板
Canvas canvas = new Canvas(blankBitmap);
// 把读取的图片画到新创建的Bitmap对象中
canvas.drawBitmap(bitmap, new Matrix(), new Paint());
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setTextSize(30);
// 通过创建的画笔,在Bitmap上写入水印
canvas.drawText("我是水印", 10, 50, paint); iv_image.setImageBitmap(blankBitmap);
} catch (Exception e) {
Toast.makeText(MainActivity.this, "获取图片失败", 0).show();
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}
效果展示:
总结
其实对于这两个简单的Demo而言,只需要针对分辨率进行优化即可,一般而言因为功能简单,系统配置只要还过的去,都是可以被正常GC的,但是对于一些经常操作图片的应用来说,还是显式的通过代码的方式来管理Bitmap的内存。最后加入Canvas进行渲染水印,不是必须的,只是加了个功能而已,直接使用bitmap对象也可以。

Android--从系统Camera和Gallery获取图片优化的更多相关文章
- Android 从Gallery获取图片
本文主要介绍Android中从Gallery获取图片 设计项目布局 <LinearLayout xmlns:android="http://schemas.android.com/ap ...
- Android--从系统Gallery获取图片
前言 在Android应用中,经常有场景会需要使用到设备上存储的图片,而直接从路径中获取无疑是非常不便利的.所以一般推荐调用系统的Gallery应用,选择图片,然后使用它.本篇博客将讲解如何在Andr ...
- Android -- 加载大图片到内存,从gallery获取图片,获取图片exif信息
1. 加载大图片到内存,从gallery获取图片 android默认的最大堆栈只有16M, 图片像素太高会导致内存不足的异常, 需要将图片等比例缩小到适合手机屏幕分辨率, 再加载. 从gallery ...
- Android 从相机或相册或获取图片(转)
参考: https://github.com/ASDbobo/GetPhotoDemo Android 8.0 调取系统摄像头和相册选择图片 9.3 使用Camera拍照
- Android总结之打开手机相册获取图片
上一篇,总结了如何打开照相机获取图片,详情请看>>>> 这篇将总结如何打开手机存储(相册)来获取手机上的图片. 打开相册 在需要这个功能的类中,我们可以自定义一个方法openA ...
- 从系统的gallery获取图片
1 ) 效果演示: 2代码演示 布局代码:
- android 中 系统日期时间的获取
import java.text.SimpleDateFormat; SimpleDateFormat formatter = new SimpleDateFormat ...
- Android开发之多媒体编程之获取图片的副本
使用BitmapFactory的decodeFile()方法获取的Bitmap对象是只读的,无法进行编辑操作 需要进行编辑的话,需要获取到该对象的一个副本 代码如下: import android.a ...
- Android打开系统的Document文档图片选择
打开Document UI 过滤图片 private void startAcitivty() { Intent intent = new Intent(); intent.setAction(&qu ...
随机推荐
- IO流2
一.IO流简介及分类 1.IO流简介 IO流: 简单理解数据从一个地方流向另外一个地方 2.IO流分类 按照数据流动的方向 分为 输入流和输出流 按照数据流动的单位分为 字节流和字符流 二.四大 ...
- C++日志打印
C++日志打印: %d 十进制有符号整数 %u 十进制无符号整数 %f 浮点数 %s 字符串 % ...
- “Hello, my first blog”------第一篇博客的仪式感
本人在校大学生一枚,开通博客,主要是想记录自己的学习过程,分享自己的学习经历.记得大一的时候,很多不懂的操作和知识,都是在博客上找到了相应的解决办法.但比较讽刺的是,很多时候,曾经解决了的问题,当再次 ...
- Capslock+ 键盘党都爱的高效利器 - 让 Windows 快捷键操作更加灵活强大
Capslock+ 键盘党都爱的高效利器 - 让 Windows 快捷键操作更加灵活强大 优化辅助 Windows 2016-06-05 91,167 微博微信QQ空间印象有道邮件 ...
- PBRT笔记(8)——材质
BSDF类 表面着色器会绑定场景中每一个图元(被赋予了这个着色器),而表面着色器则由Material类的实例来表示.它会拥有一个BSDF类对象(可能是BSSDF),用于计算表面上每一点的辐射度(颜色) ...
- 勾勾街——一个专注于免越狱免签名的苹果ios APP打包生成的网站
自涛舅舅研发的“苹果ios APP自助生成系统”上线以来,每天都有大量的用户注册和生成免越狱app,为什么? 因为我们有明显的技术优势,APP不需要上架appstore, 生成APP又不需要企业签名证 ...
- 把H5打包成IOS APP其实可以很简单!签名?越狱?都不需要!
很多小伙伴都在开发自己的app, 有的实现实现比较简单,就是一个h5页面,然后想要打包成app发布出去. 这个想法很单纯 打包生成个app这个是很简单的,网上一堆打包工具,分分钟可以完成 BUT…… ...
- ABAQUS/CAE——Context
Part(部分) 用户在Part单元内生成单个部件,可以直接在ABAQUS/CAE环境下用图形工具生成部件的几何形状,也可以从其他的图形软件输入部件.详细可参考ABAQUS/CAE用户手册第15章. ...
- connector for python实验
MySQL 是最流行的关系型数据库管理系统,如果你不熟悉 MySQL,可以阅读 MySQL 教程. 下面为大家介绍使用 mysql-connector 来连接使用 MySQL, mysql-conne ...
- ionic基于GPS定位并通过百度地图获取定位详细信息
相信所有的前端攻城狮都会碰到移动端App.里面获取用户定位信息. 那么问题来了,怎么获取用户的定位信息(经纬度)呢. 当然方法有很多,通过百度地图API 以及 高德地图 API都是可以的.但是两个获取 ...