文档章节

用decodeFileDescriptor替代decodeStream或者decodeFile

晃晃悠悠
 晃晃悠悠
发布于 2016/06/28 09:20
字数 1469
阅读 18
收藏 0

加载图片的oom总会出现 当时查了一下资料发现这个比较有意思

 

最近,在开发某App的时候,发现了一个很奇怪的bug,前面我也发了关于bitmap的总结,但是这个问题恰恰出在BitmapFactory.decodeFile(pathName)这个函数上,使用这个函数在我的应用中如果设置在activity的onCreate方法内部,会导致activity无法加载,返回上级activity。

  网上描述的大多数原因是OutOfMemoryError,但我catch不到这个error,所以可以肯定不是内存溢出引起的错误。为什么解码图像会出现这样的问题呢?关于这个问题,我纠结了一段时间。

由于调用decodeFile与decodeStream基本相似,中间过程中会引用一个设置bitmap比例的函数外最终都会调用BitmapFactory.decodeStream(is, outPadding, opts),先看一下他们的转码流程,下面这段解码分析参考的是别人一篇oom文章的:

 

  上图是整个decodeStream实现bitmap转码的流程,最终的决定权其实是在Init.c中,因为Android在启动系统的时候会去优先执行这个里面的函数,通过调用dvmStartup()方法来初始化虚拟机,最终调用到会调用到HeapSource.c中的dvmHeapSourceStartup()方法,而在Init.c中有这么两句代码:

 

gDvm.heapSizeStart = 2 * 1024 * 1024;   // Spec says 16MB; too big for us.

gDvm.heapSizeMax = 16 * 1024 * 1024;    // Spec says 75% physical mem

 

 

在另外一个地方也有类似的代码,那就是AndroidRuntime.cpp中的startVM()方法中:

 

复制代码

strcpy(heapsizeOptsBuf, "-Xmx");

property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");

//LOGI("Heap size: %s", heapsizeOptsBuf);

opt.optionString = heapsizeOptsBuf;

复制代码

 

 

 

同样也是默认值为16M,虽然目前我看到了两个可以启动VM的方法,具体Android何时会调用这两个初始化VM的方法,还不是很清楚。不过可以肯定的一点就是,如果启动DVM时未指定参数,那么其初始化堆最大大小应该就是16M,那么我们在网上查到了诸多关于解码图像超过8M就会出错的论断是如何得出来的呢?

我们来看看HeapSource.c中的这个方法的注释:

 

复制代码

/*

* External allocation tracking

*

* In some situations, memory outside of the heap is tied to the

* lifetime of objects in the heap.  Since that memory is kept alive

* by heap objects, it should provide memory pressure that can influence

* GCs.

*/

static bool

externalAllocPossible(const HeapSource *hs, size_t n)

{

    const Heap *heap;

    size_t currentHeapSize;

   /* Make sure that this allocation is even possible.

     * Don’t let the external size plus the actual heap size

     * go over the absolute max.  This essentially treats

     * external allocations as part of the active heap.

     *

     * Note that this will fail "mysteriously" if there’s

     * a small softLimit but a large heap footprint.

     */

    heap = hs2heap(hs);

    currentHeapSize = mspace_max_allowed_footprint(heap->msp);

    if (currentHeapSize + hs->externalBytesAllocated + n <=

            heap->absoluteMaxSize)

    {

        return true;

    }

    HSTRACE("externalAllocPossible(): "

            "footprint %zu + extAlloc %zu + n %zu >= max %zu (space for %zu)\n",

            currentHeapSize, hs->externalBytesAllocated, n,

            heap->absoluteMaxSize,

            heap->absoluteMaxSize -

                    (currentHeapSize + hs->externalBytesAllocated));

    return false;

}

复制代码

 

 

 

标为红色的注释的意思应该是说,为了确保我们外部分配内存成功,我们应该保证当前已分配的内存加上当前需要分配的内存值,大小不能超过当前堆的最大内存值,而且内存管理上将外部内存完全当成了当前堆的一部分。也许我们可以这样理解,Bitmap对象通过栈上的引用来指向堆上的Bitmap对象,而Bitmap对象又对应了一个使用了外部存储的native图像,实际上使用的是byte[]来存储的内存空间,如下图:

 

 

当然我们承认不好的程序总是程序员自己错误的写法导致的 ,不过我们倒是非常想知道如何来规避这个问题,那么接下来就是解答这个问题的关键。

今天无意中看到stackoverflow上有人也曾经遇到过这个问题,而这个给了一个很好的解决方案,但他也不知道这个BUG该怎么解释:

I had this same issue and solved it by avoiding the BitmapFactory.decodeStream or decodeFile functions and instead used BitmapFactory.decodeFileDescriptor

decodeFileDescriptor looks like it calls different native methods than the decodeStream/decodeFile.

Anyway what worked was this (note that I added some options as some had above, but that's not what made the difference. What is critical is the call to Bitmap.decodeFileDescriptor instead of decodeStream or decodeFile):

 

复制代码

private void showImage(String path)   {
    Log.i("showImage","loading:"+path);
    BitmapFactory.Options bfOptions=new BitmapFactory.Options();
    bfOptions.inDither=false;                     
    bfOptions.inPurgeable=true;                 
    bfOptions.inInputShareable=true;             
    bfOptions.inTempStorage=new byte[32 * 1024]; 


    File file=new File(path);
    FileInputStream fs=null;
    try {
        fs = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    try {
        if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
    } catch (IOException e) {
        e.printStackTrace();
    } finally{ 
        if(fs!=null) {
            try {
                fs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    im.setImageBitmap(bm);
    bm=null;
}

复制代码

 

 

 

I think there is a problem with the native function used in decodeStream/decodeFile. I have confirmed that a different native method is called when using decodeFileDescriptor. Also what I've read is "that Images (Bitmaps) are not allocated in a standard Java way but via native calls; the allocations are done outside of the virtual heap, but are counted against it!"

我拙劣的翻译了一下:

我遇到了同样的问题,通过规避BitmapFactory.decodeStream或者decodeFile函数,使用BitmapFactory.decodeFileDescriptor解决的,decodeFileDescriptor相比decodeStream/decodeFile来说,看起来它调用了不同的本地方法。无论如何,它是这样工作的(注意,像上边的一样,我增加了一些设置,但那不是使这个不同的地方。)关键的就是它调用Bitmap.decodeFileDescriptor而不是decodeStream or decodeFile)。

我想这可能是decodeStream/decodeFile中本地函数的问题。我很确定当使用decodeFileDescriptor时一个不同的本地方法被调用。我读到的也是“图片(Bitmaps)并不是指派给一个标准的java路径,但是是通过本地调用的;这个分配是在虚拟的堆外完成的,但是是被认为针对它的!”。

 

还是点到为止吧,大家都应该明白我的题目的意思了,最主要的是这个错误没办法去验证究竟什么地方发生了错误,但是可以规避这种错误,希望大家都自己去测试一下,验证一下,毕竟自己做过验证的才能算是放心的。

 

参考资料:http://www.cnblogs.com/-OYK/archive/2012/12/03/2798903.html

本文转载自:http://www.cnblogs.com/akira90/archive/2013/03/15/2960666.html?utm_source=tuicool&utm_medium=refe...

晃晃悠悠
粉丝 0
博文 18
码字总数 3220
作品 0
海淀
程序员
私信 提问
android BitmapFactory的OutOfMemoryError: bitmap ...

网上有很多解决android加载bitmap内存溢出的方法,搜了一圈做下整理总结。项目里需求是拍摄多图之后上传,部分手机会内存溢出。 常用一种解决方法:即将载入的图片缩小,这种方式以牺牲图片的...

jeffzhao
2012/09/28
6.6K
3
android在线测试之图像 camera2解析

1.ImageView类用于显示各种图像,例如:图标,图片,下面对于ImageView类加载图片方法的描述有: void setImageResource(int resld): 设置Drawanble图像。参数resld表示drawable的标识符。 ...

android开发
2017/12/18
0
0
问题 android 图片内存溢出 BitmapFactory.decodeStream

mPreview = BitmapFactory.decodeStream(HttpsImage.getStream(mPreviewUrl)); 崩溃日志 在android中的大图片一般都要经过压缩才能显示,不然容易发生oom一般我们压缩的时候都只关注其尺寸方...

kylinhuang
2016/10/09
70
0
Android 图片处理避免出现oom的方法

1. 通过设置采样率压缩 res资源图片压缩 decodeResource uri图片压缩 decodeStream 本地File url图片压缩 根据显示的图片大小进行SampleSize的计算 附:宽高dp值转px的工具类 以上三种工具类的...

燊在锦官城_
2017/09/11
0
0
Android加载图片导致内存溢出(Out of Memory异常)

Android在加载大背景图或者大量图片时,经常导致内存溢出(Out of Memory Error),本文根据我处理这些问题的经历及其它开发者的经验,整理解决方案如下(部分代码及文字出处无法考证): 方...

天下杰论
2013/06/20
6.9K
2

没有更多内容

加载失败,请刷新页面

加载更多

Spring boot 静态资源访问

0. 两个配置 spring.mvc.static-path-patternspring.resources.static-locations 1. application中需要先行的两个配置项 1.1 spring.mvc.static-path-pattern 这个配置项是告诉springboo......

moon888
今天
3
0
hash slot(虚拟桶)

在分布式集群中,如何保证相同请求落到相同的机器上,并且后面的集群机器可以尽可能的均分请求,并且当扩容或down机的情况下能对原有集群影响最小。 round robin算法:是把数据mod后直接映射...

李朝强
今天
4
0
Kafka 原理和实战

本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/bV8AhqAjQp4a_iXRfobkCQ 作者简介:郑志彬,毕业于华南理工大学计算机科学与技术(双语班)。先后从事过电子商务、开放平...

vivo互联网技术
今天
20
0
java数据类型

基本类型: 整型:Byte,short,int,long 浮点型:float,double 字符型:char 布尔型:boolean 引用类型: 类类型: 接口类型: 数组类型: Byte 1字节 八位 -128 -------- 127 short 2字节...

audience_1
今天
9
0
太全了|万字详解Docker架构原理、功能及使用

一、简介 1、了解Docker的前生LXC LXC为Linux Container的简写。可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。相当于C++中的NameSpa...

Java技术剑
今天
27
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部