博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android--从系统Camera和Gallery获取图片优化
阅读量:5806 次
发布时间:2019-06-18

本文共 4898 字,大约阅读时间需要 16 分钟。

hot3.png

前言

  之前有两篇博客讲解了如何从系统内已有的Camera和Gallery应用中获取图片的例子,看到评论里有朋友说有时候会报错,导致程序崩溃的 问题。本篇博客主要就这个问题分析讲解一下,最后将以一个简单的Demo演示。关于从系统内已有的Camera和Gallery应用中获取图片还不了解的 朋友,可以先看看另外两篇博客:、。

 

分析出错原因

  之前讲到的从系统现有的Camera和Gallery应用中获取图片的Demo中,均直接使用系统应用返回的Uri,通过 ImageView.setImageURI(Uri)方法显示在界面上。而对于Android设备来说,向内存中加载一张图片,消耗的内存并不受图片的 大小而影响,影响它的是图片的分辨率,图片的分辨率越大加载到内存所占用的内存将越多。使用ImageView.setImageURI(Uri)方法将 导致了一个严重的错误,虽然ImageView直接引用图片的Uri,它会对图片进行一部分优化,使得它可以正常显示,但是这种办法不利于Bitmap资 源的回收。所以在重复操作之后,经历过多次的GC,也没有办法回收出足够加载图片的内存,导致应用崩溃。

 

解决方案

  既然已经知道导致程序崩溃的原因是内存溢出导致的,那么只需要维护好Uri所代表的图片内存即可。具体优化流程如下:

  1、系统中现有的Camera和Gallery应用获取图片返回的都是一个Uri类型的数据,它是一个内容提供者的路径,可以使用ContentResolver获取它,这个以前有讲过,不了解的朋友可以看看另外一篇博客:。而在Context中,可以使用getContentResolver()方法获取到当前的内容解析者,并通过它的openInputStream()方法获取到图片的输入流,通过输入流可以获取到一个Bitmap对象。

  2、上面提到,Android中加载图片到内存中所占内存的大小取决于图片的分辨率,所有得到Bitmap还不能直接使用它,必须对其进行优 化,以最大适应当前设备的屏幕分辩率又不会导致加载过多像素而导致内存不足的情况。关于加载大分辨率到内存还不了解的朋友可以参见另外一篇博客:。

  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对象也可以。

转载于:https://my.oschina.net/u/1182603/blog/182050

你可能感兴趣的文章
JQ学习笔记--进阶
查看>>
仿查询分析器的C#计算器——3.词法分析
查看>>
Python中常用的命令
查看>>
python 基础复习 06
查看>>
JAVA考试必备(一)
查看>>
iphone uitableview 设置分区标题(section title)
查看>>
新注册第一帖----------------------乱码新手自学.net 之Linq 入门篇
查看>>
关于new enhancement的一些知识
查看>>
ios 程序发布使用xcode工具Application Loader 正在通过ITUNES STORE进行鉴定错误
查看>>
Centos_7安装python-pip
查看>>
input子系统 KeyPad-Touch上报数据格式与机制
查看>>
儒家思想
查看>>
关于游戏
查看>>
[转]CNN目标检测(一):Faster RCNN详解
查看>>
NYOJ_214_单调递增子序列(二)
查看>>
数字三角形系列
查看>>
自动化测试时Ios设备无法调出键盘问题
查看>>
android将asseet当中的数据库文件拷到程序目录
查看>>
USACO 1.4.1 Packing Rectangles
查看>>
iOS多线程编程Part 2/3 - NSOperation
查看>>