目录介绍
- 7.0.0.1 加载bitmap图片的时候需要注意什么?为何bitmap容易造成OOM?如何计算Bitmap占用内存?
- 7.0.0.2 如何理解recycle释放内存问题?图片加载到内存其实有两部分数据,这是为何?
- 7.0.0.3 如何在不压缩图片的情况下加载高清大图?加载图的机制是什么,为何不会内存泄漏?
- 7.0.0.7 LRU算法的原理?核心思想是什么?如果缓存满了的话,什么方法来管理移除最近最少使用的item和添加新的item?
好消息
- 博客笔记大汇总【15年10月到至今】,包括 Java 基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
- 链接地址: https://github.com/yangchong2...
- 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!所有的笔记将会更新到GitHub上,同时保持更新,欢迎同行提出或者push不同的看法或者笔记!
7.0.0.1 加载bitmap图片的时候需要注意什么?为何bitmap容易造成OOM?如何计算Bitmap占用内存?
-
注意问题
- 直接加载大容量的高清Bitmap很容易出现显示不完整、内存溢出OOM的问题,所以最好按一定的采样率将图片缩小后再加载进来
- 为减少流量消耗,可对图片采用内存缓存策略,又为了避免图片占用过多内存导致内存溢出,最好以软引用方式持有图片
- 如果还需要网上下载图片,注意要开子线程去做下载的耗时操作 技术博客大总结
- 为何bitmap容易造成OOM
-
如何计算Bitmap占用内存
-
1.1 如何计算占用内存
- 如果图片要显示下Android设备上,ImageView最终是要加载Bitmap对象的,就要考虑单个Bitmap对象的内存占用了,如何计算一张图片的加载到内存的占用呢?其实就是所有像素的内存占用总和:
- bitmap内存大小 = 图片长度 x 图片宽度 x 单位像素占用的字节数
- 起决定因素就是最后那个参数了,Bitmap'常见有2种编码方式:ARGB_8888和RGB_565,ARGB_8888每个像素点4个byte,RGB_565是2个byte,一般都采用ARGB_8888这种。那么常见的1080*1920的图片内存占用就是:1920 x 1080 x 4 = 7.9M
-
1.2 上面方法计算内存对吗
-
我看到好多博客都是这样计算的,但是这样算对吗?有没有哥们试验过这种方法正确性?我觉得看博客要对博主表示怀疑,论证别人写的是否正确。更多详细可以看我的GitHub: https://github.com/yangchong211
- 说出我的结论:上面1.1这种说法也对,但是不全对,没有说明场景,同时也忽略了一个影响项:Density。接下来看看源代码。
- inDensity默认为图片所在文件夹对应的密度;inTargetDensity为当前系统密度。
- 加载一张本地资源图片,那么它占用的内存 = width height nTargetDensity/inDensity nTargetDensity/inDensity 一个像素所占的内存。
@Nullable public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value, @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) { validate(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); }
-
正确说法,这个注意呢?计算公式如下所示
- 对资源文件:width height nTargetDensity/inDensity nTargetDensity/inDensity 一个像素所占的内存;
- 别的:width height 一个像素所占的内存;
-
-
1.3 一个像素占用多大内存 技术博客大总结
-
Bitmap.Config用来描述图片的像素是怎么被存储的?
- ARGB_8888: 每个像素4字节. 共32位,默认设置。
- Alpha_8: 只保存透明度,共8位,1字节。
- ARGB_4444: 共16位,2字节。
- RGB_565:共16位,2字节,只存储RGB值。
-
-
7.0.0.2 如何理解recycle释放内存问题?图片加载到内存其实有两部分数据,这是为何?
-
如何理解recycle释放内存问题?
- 在Android2.3.3(API 10)及之前的版本中,Bitmap对象与其像素数据是分开存储的,Bitmap对象存储在Dalvik heap中,而Bitmap对象的像素数据则存储在Native Memory(本地内存)中或者说Derict Memory(直接内存)中,这使得存储在Native Memory中的像素数据的释放是不可预知的,我们可以调用recycle()方法来对Native Memory中的像素数据进行释放,前提是你可以清楚的确定Bitmap已不再使用了,如果你调用了Bitmap对象recycle()之后再将Bitmap绘制出来,就会出现”Canvas: trying to use a recycled bitmap”错误,而在Android3.0(API 11)之后,Bitmap的像素数据和Bitmap对象一起存储在Dalvik heap中。
-
图片加载到内存其实有两部分数据,这是为何? 技术博客大总结
- 一个图片加载到内存里,其实是有两部分数据组成,一部分是图片的相关描述信息,另一部分就是最重要的像素信息(这部分是有byte数组组成的),android系统为了提高对图片的处理效率,对于图片的处理都是调用了底层的功能(由 C语言 实现的),也就是说一个图片加载到内存里后是使用两部分的内存区域,简单的说:一部分是java可用的内存区,一部分是c可用的内存区,这两个内存区域是不能相互直接使用的,这个bitmap对象是由java分配的,当然不用的时候系统会自动回收了,可是那个对应的C可用的内存区域jvm是不能直接回收的,这个只能调用底层的功能释放。所以你要调用recycle方法来释放那一部分内存。
-
查看源码如下所示
- 翻译这段解释:释放bitmap内存的时候,它会释放和这个bitmap有关的native内存,同时它会清理有关数据对象的引用,但是这里处理数据对象的引用,并不是立即清理数据(他并不是调用玩recycle()方法,就直接清理这个内存,他只是给垃圾回收机制发送一个指令,让它在bitmap没有对象引用的时候,来进行垃圾回收)。当调用recycle()方法之后,这个bitmap就会被表明为“死亡状态”。这个时候你在调用bitmap其他相关的方法,例如果get像素()或set像素()就会抛出一个异常。同时这个操作是不可逆的,所以一定百分之百确定这个bitmap在以后的场景下,不会被你的程序在使用到,再去调用recycle()方法。所以谷歌源码中建议我们,可以不用去主动调用recycle()方法,因为在没有引用的情况下,我们的垃圾回收机制会主动的清理内存。
- 通过看源码,我们会发现,这个方法首先将这个Bitmap的引用置为null,然后调用了nativeRecycle(mNativeBitMap)方法,这个方法很明显是个JNI调用,会调用底层的c或者c++代码就可以做到对该内存的立即回收,而不需要等待那不确定啥时候会执行的GC来回收了。
7.0.0.3 如何在不压缩图片的情况下加载高清大图?加载图的机制是什么,为何不会内存泄漏?
-
如何在不压缩图片的情况下加载高清大图?
- 使用BitmapRegionDecoder,主要用于显示图片的某一块矩形区域,如果你需要显示某个图片的指定区域,那么这个类非常合适。
- 加载图的机制是什么,为何不会内存泄漏?
-
自定义可拖动的显示高清大图的View 技术博客大总结
- 提供一个设置图片的入口,setInputStream里面去获得图片的真实的宽度和高度,以及初始化我们的mDecoder
- 重写onTouchEvent,在里面根据用户移动的手势,去更新显示区域的参数。在onMeasure里面为我们的显示区域的rect赋值,大小为view的尺寸
- 每次更新区域参数后,调用invalidate,onDraw里面去regionDecoder.decodeRegion拿到bitmap,然后draw。
7.0.0.7 LRU算法的原理?核心思想是什么,谈谈你的思路?
-
为减少流量消耗,可采用缓存策略。常用的缓存算法是LRU(Least Recently Used):
-
核心思想:当缓存满时, 会优先淘汰那些近期最少使用的缓存对象。主要是两种方式:
- LruCache(内存缓存):LruCache类是一个线程安全的泛型类:内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,并提供get和put方法来完成缓存的获取和添加操作,当缓存满时会移除较早使用的缓存对象,再添加新的缓存对象。
- DiskLruCache(磁盘缓存): 通过将缓存对象写入文件系统从而实现缓存效果
-
-
大概过程如下? 技术博客大总结
- LruCache是android提供的一个缓存 工具 类,其算法是最近最少使用算法。它把最近使用的对象用“强引用”存储在LinkedHashMap中,并且把最近最少使用的对象在缓存值达到预设定值之前就从内存中移除。
-
如果缓存满了的话,什么方法来管理移除最近最少使用的item和添加新的item?
-
trimToSize()方法,删除最年长的条目,直到剩余条目的总数达到或低于请求的大小
public void trimToSize(int maxSize) { while(true) { Object key; Object value; synchronized(this) { if (this.size < 0 || this.map.isEmpty() && this.size != 0) { throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if (this.size <= maxSize || this.map.isEmpty()) { return; } Entry<K, V> toEvict = (Entry)this.map.entrySet().iterator().next(); key = toEvict.getKey(); value = toEvict.getValue(); this.map.remove(key); //计算现在缓存的大小,然后减掉多余的,内部调用的是sizeOf()方法 this.size -= this.safeSizeOf(key, value); ++this.evictionCount; } //如果你想在在我们的缓存中实现二级缓存,可以实现此方法,源码中是空方法。 this.entryRemoved(true, key, value, (Object)null); } }
-
关于其他内容介绍
01.关于博客汇总链接
以上所述就是小编给大家介绍的《07.Android之多媒体问题》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Java播放多媒体
- Java处理多媒体文件
- 音视频基础之多媒体封装格式
- FFmpeg 3.3.4 发布,多媒体处理工具
- FFmpeg 3.4 发布,多媒体处理工具合集
- GStreamer 1.14 发布,多媒体开发框架
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
算法:C语言实现
塞奇威克 / 霍红卫 / 机械工业出版社 / 2009-10 / 79.00元
《算法:C语言实现(第1-4部分)基础知识、数据结构、排序及搜索(原书第3版)》细腻讲解计算机算法的C语言实现。全书分为四部分,共16章。包括基本算法分析原理,基本数据结构、抽象数据结构、递归和树等数据结构知识,选择排序、插入排序、冒泡排序、希尔排序、快速排序方法、归并和归并排序方法、优先队列与堆排序方法、基数排序方法以及特殊用途的排序方法,并比较了各种排序方法的性能特征,在进一步讲解符号表、树等......一起来看看 《算法:C语言实现》 这本书的介绍吧!