性能优化-Bitmap内存管理及优化
Bitmap作为重要Android应用之一,在很多时候如果应用不当,很容易造成内存溢出,那么这篇文章的目的就在于探讨Bitmap的有效运用及其优化
缓存介绍
当多次发送请求的时候,请求同一内容,为了使资源得到合理利用,那么就需要设置缓存,避免同一内容被多次请求
在这里使用一个Http的缓存策略,对http自带的缓存策略做一个简单的使用介绍,从而引出今天的主角
http自带缓存的使用前提:服务器设置了缓存时间
response.addHeader("Cache-control", "max-age=10"); //HttpServletResponse response
以上代表了在10秒重内不会再请求服务器,此时客户端开启了缓存的话,在10内就不会重复请求了
http自带缓存的策略的使用:
- 打开缓存,Android在默认情况下HttpResponseCache(网络请求响应缓存)是关闭的
try {File cacheDir = new File(getCacheDir(), "http");//缓存目录,在应用目录的cache文件夹下生成http文件夹long maxSize = 10 * 1024 * 1024;//缓存大小:byteHttpResponseCache.install(cacheDir, maxSize );Log.d(TAG, "打开缓存");
} catch (IOException e) {e.printStackTrace();
}
- 发送请求,这里以向服务器请求一张图片为例
此时,会连接服务器,获取服务器里面的内容
new Thread(new Runnable() {@Overridepublic void run() {try {BitmapFactory.decodeStream((InputStream) new URL("http://192.168.1.7:8080/test.png").getContent());Log.d(TAG, "下载图片");} catch (Exception e) {e.printStackTrace();}}
}).start();
- 删除缓存,就算现在不删,也要找时间清理
HttpResponseCache cache = HttpResponseCache.getInstalled();
if(cache!=null){try {cache.delete();Log.d(TAG, "清空缓存");} catch (IOException e) {e.printStackTrace();}
}
经过以上步骤,就走完了http缓存的一个流程,在实际应用中,一般会采用自己设计缓存的方法,这里只是引出缓存这个概念
Bitmap优化着手点
在使用Bitmap的时候,容易使得Bitmap超过系统分配的内存阈值,发么此时就产生了常见的OOM报错,因此,优化Bitmap的思路也就在于如何避免OOM的发生
那么避免OOM发生就要从图片本身下手了,常见的处理方式便是将图片进行压缩,常用的压缩算法有三种:
质量压缩
质量压缩是通过同化像素点周围的相近的像素点,从而达到降低文件大小的作用,也就是说其本身的像素多少并没有改变,压缩出来的图片虽然大小发生了改变,但分辨率没有发生改变,而在Bitmap中,其是按照像素大小,即图片的像素多少来计算内存空间的,因此这种方法并不能有效避免OOM,这也就是为何只改变图片大小对于Bitmap的内存使用没有作用的原因
那么质量压缩有什么作用呢?其实它的作用就是减少存储体积,方便传输或者保存尺寸压缩
尺寸压缩的思路就是使用Canvas读取现在的bitmap,然后对其尺寸进行修改,这里是真实的改变了图片的像素大小,所以在Bitmap使用的时候,就会得到改变尺寸后的大小,那么就可以对Bitmap进行有效优化,这种操作一般用于缓存缩略图采样率压缩
设置图片的采样率,降低图片像素,其原理和尺寸压缩类似,不过实现的方式不同
Bitmap的Java层源代码分析
由于Bitmap的底层CPP代码涉及到的东西略多,这里就简单介绍下Java层的源码就好
在Bitmap使用的使用,通常会使用如下三个方法加载来自不同地方的图片
BitmapFactory.decodeFile(path)
BitmapFactory.decodeResource(res, id)
BitmapFactory.decodeStream(is)
那么在BitmapFactory
源代码中,这三个方法是怎么处理的呢?
打开源代码,我们可以看到:
public static Bitmap decodeFile(String pathName) {return decodeFile(pathName, null);
}
···
public static Bitmap decodeResource(Resources res, int id) {return decodeResource(res, id, null);
}
···
public static Bitmap decodeStream(InputStream is) {return decodeStream(is, null, null);
}
在这里又传递了一次参数,那么找到这三个传递参数的方法
public static Bitmap decodeFile(String pathName, Options opts) {Bitmap bm = null;InputStream stream = null;try {stream = new FileInputStream(pathName);bm = decodeStream(stream, null, opts);} catch (Exception e) {/* do nothing.If the exception happened on open, bm will be null.*/Log.e("BitmapFactory", "Unable to decode stream: " + e);} finally {if (stream != null) {try {stream.close();} catch (IOException e) {// do nothing here}}}return bm;
}
在decodeFile()
方法中,将传入文件转化成了流,送到了decodeStream()
进行处理,其他就没做什么了,那么我们再看看decodeResource()
做了啥
public static Bitmap decodeResource(Resources res, int id, Options opts) {Bitmap bm = null;InputStream is = null; try {final TypedValue value = new TypedValue();is = res.openRawResource(id, value);bm = decodeResourceStream(res, value, is, null, opts);} catch (Exception e) {/* do nothing.If the exception happened on open, bm will be null.If it happened on close, bm is still valid.*/} finally {try {if (is != null) is.close();} catch (IOException e) {// Ignore}}if (bm == null && opts != null && opts.inBitmap != null) {throw new IllegalArgumentException("Problem decoding into existing bitmap");}return bm;
}
由以上代码看出,decodeResource()
也只是中转,把参数设置好,传到decodeResourceStream()
中去了,那么顺藤摸瓜,看一看这个方法里干了啥,于是我们找到了这个方法
public static Bitmap decodeResourceStream(Resources res, TypedValue value,InputStream is, Rect pad, Options opts) {if (opts == null) {opts = new Options();}if (opts.inDensity == 0 && value != null) {final int density = value.density;if (density == TypedValue.DENSITY_DEFAULT) {opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;} else if (density != TypedValue.DENSITY_NONE) {opts.inDensity = density;}}if (opts.inTargetDensity == 0 && res != null) {opts.inTargetDensity = res.getDisplayMetrics().densityDpi;}return decodeStream(is, pad, opts);
}
查看代码发现,这里其实也没有干嘛,设置了Options参数,这里涉及到一个像素密度和分辨率的转化问题,其中,DisplayMetrics.DENSITY_DEFAULT = 160
,这个问题网上有很多讨论,这里简明说明一下
px = dp * Density
详细介绍可以参阅此文:dpi、dip、分辨率、屏幕尺寸、px、density关系以及换算
综合上面的分析,所有的线索都指向了decodeStream()
这个方法,那么我们就来看看这个方法干了啥吧
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {// we don't throw in this case, thus allowing the caller to only check// the cache, and not force the image to be decoded.if (is == null) {return null;}Bitmap bm = null;Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");try {if (is instanceof AssetManager.AssetInputStream) {final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();bm = nativeDecodeAsset(asset, outPadding, opts);} else {bm = decodeStreamInternal(is, outPadding, opts);}if (bm == null && opts != null && opts.inBitmap != null) {throw new IllegalArgumentException("Problem decoding into existing bitmap");}setDensityFromOptions(bm, opts);} finally {Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);}return bm;
}
看一看上面的代码,先是判断了输出流,然后新建Bitmap对象,之后开始写入跟踪信息,这里可以忽略这个写入跟踪信息的玩意儿,下一步就是校验输入流的来源,是assets里面的,还是其他的,因为位置不一样,其处理方法不一样,这里的nativeDecodeAsset()
方法就是JNI的方法了,而decodeStreamInternal
又是啥呢?再往下看看
private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {// ASSERT(is != null);byte [] tempStorage = null;if (opts != null) tempStorage = opts.inTempStorage;if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];return nativeDecodeStream(is, tempStorage, outPadding, opts);
}
这里就是设置了一个数组,以供JNI使用,然后又传递到了JNI里面去了,也就是说这里调用了两个JNI的方法,一个是assets目录,一个是非assets目录
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,Rect padding, Options opts);
private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts);
那么JNI中做了什么事情呢?
static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,jobject padding, jobject options) {jobject bitmap = NULL;std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));if (stream.get()) {std::unique_ptr<SkStreamRewindable> bufferedStream(SkFrontBufferedStream::Create(stream.release(), SkCodec::MinBufferedBytesNeeded()));SkASSERT(bufferedStream.get() != NULL);bitmap = doDecode(env, bufferedStream.release(), padding, options);}return bitmap;
}
nativeDecodeAsset()
处理与其类似,这里就不再展开了,这篇文章进行了详细分析:android 图片占用内存大小及加载解析
那么,Java层代码到此分析结束,在Java层,其最终调用的就是decodeStream()
方法了
decodeStream()
方法参数分析
由上面的代码分析,我们已经知道,BitmapFactory的调用参数可以设置的由三个InputStream
,Rect
和Options
那么Options又有什么参数可以设置呢?
inDensity
:bitmap的像素密度
inTargetDensity
:bitmap输出的像素密度
质量压缩实现方法
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);public void qualitCompress(Bitmap bmp, File file){int quality = 50; //0~100ByteArrayOutputStream baos = new ByteArrayOutputStream();bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos); //图片格式可以选择JPEG,PNG,WEBPtry {FileOutputStream fos = new FileOutputStream(file);fos.write(baos.toByteArray());fos.flush();fos.close();} catch (Exception e) {e.printStackTrace();}
}
尺寸压缩的实现方法
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);public static void sizeCompress(Bitmap bmp,File file){int ratio = 4; //缩小的倍数Bitmap result = Bitmap.createBitmap(bmp.getWidth() / ratio, bmp.getHeight() / ratio,Bitmap.Config.ARGB_8888);//这里设置的是Bitmap的像素格式Canvas canvas = new Canvas(result);RectF rect = new RectF(0, 0, bmp.getWidth() / ratio, bmp.getHeight() / ratio);canvas.drawBitmap(bmp, null, rect, null); //通过Canvas重新画入ByteArrayOutputStream baos = new ByteArrayOutputStream();result.compress(Bitmap.CompressFormat.JPEG, 100, baos);try {FileOutputStream fos = new FileOutputStream(file);fos.write(baos.toByteArray());fos.flush();fos.close();} catch (Exception e) {e.printStackTrace();}
}
采样率压缩的实现方法
public static void rateCompress(String filePath, File file){int inSampleSize = 8; //设置采样率,数值越大,像素越低BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = false; //为true的时候不会真正加载图片,而是得到图片的宽高信息options.inSampleSize = inSampleSize; //采样率Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);ByteArrayOutputStream baos = new ByteArrayOutputStream();bitmap.compress(Bitmap.CompressFormat.JPEG, 100 ,baos); // 把压缩后的数据存放到baos中try {if(file.exists()){file.delete();} else {file.createNewFile();}FileOutputStream fos = new FileOutputStream(file);fos.write(baos.toByteArray());fos.flush();fos.close();} catch (Exception e) {e.printStackTrace();}
}
总结
以上就是系统自带的压缩算法实现Bitmap的处理
质量压缩:适合网络传输
尺寸压缩和采样率压缩:适合生成缩略图
转载于:https://www.cnblogs.com/cj5785/p/10664635.html
性能优化-Bitmap内存管理及优化相关推荐
- Android进阶——性能优化之内存管理机制和垃圾采集回收机制(六)
文章大纲 引言 一.内存泄漏和内存溢出概述 二.Java运行时内存模型 1.线程私有数据区 1.1.程序计数器PC 1.2.虚拟机栈 1.3 本地方法栈 2.所有线程共享数据区 2.1.Java堆 2 ...
- 关于mysql内存管理_MYSQL内存管理及优化
MYSQL内存管理及优化 内存是影响数据库性能的主要资源,也是mysql性能优化的一个重要方面: 内存优化的原则 将尽量多的内存分配给mysql做缓存,但是要给操作系统和其他程序的运行预留足够的内存, ...
- 【Unity】Unity内存管理与优化(一)内存域、堆栈、垃圾回收、内存泄漏、内存碎片
文章目录 Unity内存 内存域 - 托管域 - 本地域 - 外部库 - 跨桥操作 堆和栈 - 栈 - 堆 - 堆栈的使用 垃圾回收 - Mono内存分配过程 - 内存泄漏 - 内存碎片 - 运行时垃 ...
- Mysql 内存管理及优化
Mysql 内存管理及优化 1)内存优化原则 1) 将尽量多的内存分配给 MySQL 做缓存,但要给操作系统和其他程序预留足够内存. 2) MyISAM 存储引擎的数据文件读取依赖于操作系统自身的 I ...
- 【Linux 内核 内存管理】优化内存屏障 ③ ( 编译器屏障 | 禁止 / 开启内核抢占 与 方法保护临界区 | preempt_disable 禁止内核抢占源码 | 开启内核抢占源码 )
文章目录 一.禁止 / 开启内核抢占 与 方法保护临界区 二.编译器优化屏障 三.preempt_disable 禁止内核抢占 源码 四.preempt_enable 开启内核抢占 源码 一.禁止 / ...
- C语言 --- 动态内存管理(上)+优化版通讯录+笔试题
文章目录 前言 一.为什么存在动态内存分配 二.动态内存函数的介绍 2.1.malloc函数+free函数 2.2.calloc函数+free函数 2.3.realloc函数 三.常见的动态内存错误 ...
- Unity 3D中的内存管理与优化游戏运行性能的经验
Unity3D在内存占用上一直被人诟病,特别是对于面向移动设备的游戏开发,动辄内存占用飙上一两百兆,导致内存资源耗尽,从而被系统强退造成极差的体验.类似这种情况并不少见,但是绝大部分都是可以避免的.虽 ...
- Android性能优化OOM内存管理——ADJ
前言 OOM_ADJ (Out of Memory Adjustment)是android系统在内存不足情况下进行内存调整的重要参数.在处理app启动速度的时候,可以设置主线程的优先级,保证主线程占用 ...
- Android 内存管理之优化建议
OOM(OutOfMemory)转:http://hukai.me/android-performance-oom/ 前面我们提到过使用getMemoryClass()的方法可以得到Dalvik He ...
最新文章
- ajax更新,AJAX网址更新(AJAX URL update)
- linux 进程创建 进程启动 监控
- android base64解密,android Base64 AES加密解密
- Pattern-No.03 设计模式之策略模式
- AngularJS学习笔记一:简单入门
- java mysql 文本导入数据语句_Java利用MYSQL LOAD DATA LOCAL INFILE实现大批量导入数据到MySQL...
- linux—用nc命令监控检测服务器端口
- Python案例:房源信息分析
- k8s核心技术-Helm(概述)---K8S_Google工作笔记0044
- 50岁的哆啦A梦“撞”上63岁的卡西欧,阿里云数据中台告诉你火花能有多大
- HDU 操作系统实验二 -设计一个系统调用,返回指定进程的相关时间信息
- 11年瑞纳手动挡值多少钱_10年瑞纳值多少钱(10年的手动高配瑞纳,跑了4万公里,现在值多少钱?)...
- HSB/HSV/HSL区别
- 智慧养老模式和智慧养老系统
- 机器学习笔记马尔可夫链蒙特卡洛方法(二)马尔可夫链与平稳分布
- bigemap如何应用卫星图像到Auto CAD
- JavaSwimg jacob调用模板,斑马打印机打印条码
- gis可达性分析步骤_CYD软件技能 | 城市动态设计分析制图 Design for the Urban Dynamics...
- 新建vivado工程仿真或修改原仿真代码后,仿真波形不更新问题(已解决)
- js实现浏览器中的复制粘贴
热门文章
- ipad运行python爬虫_Python爬虫之UserAgent的使用实例
- python php array,python处理PHP数组文本文件实例
- 手游 android 分辨率 适配,安卓不得不玩的神作,十大超高画质手游(安卓)
- linux vga 分辨率低,vga输出 1440x900 分辨率问题
- 培养产品思维,每个人都应该是产品经理
- java文件上传下载服务并发超时,先收藏了
- 解决The server cannot be started because one or more of the ports are invalid.
- SqLite中的事务
- python基础练习(三)
- 领计算机二级证材料,计算机二级证书怎么领