在开发某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 boolexternalAllocPossible(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路径,但是是通过本地调用的;这个分配是在虚拟的堆外完成的,但是是被认为针对它的!”。

Android转换位图BUG,知其然不知其所以然相关推荐

  1. 知其然而不知其所以然

    知其然而不知其所以然 [词 目]知其然而不知其所以然 [读 音]zhī qí rán ér bù zhī qí suǒ yǐ rán [释 义]然:这样,如此.知道是这样,但不知道为什么是这样.只知道 ...

  2. 苏州蜗牛面试感想 ------- 知其然而不知其所以然

    今天去面试了..公司环境不错..挺个性的...氛围也不错... 两个面试管  给我感觉一个比较随和 另外一个比较严肃... 弄的我蛮紧张的 由于公司是游戏开发 问了一些线程. 程序设计,,还有设计模式 ...

  3. Android App开发之位图加工Bitmap中转换位图的像素色彩、裁剪内部区域、利用矩阵变换位图的讲解及实战(附源码和演示)

    需要图片集和源码请点赞关注收藏后评论区留言~~~ 一.转换位图的像素色彩 给图片添加装饰物,只是在局部变换,如果想让图片一边保持轮廓一边改变色彩,就要深入图像的每个像素点,将这些像素点统统采取某种算法 ...

  4. 终于明白那些年知其然而不知其所以然的iOS内存管理方式

    终于明白那些年知其然而不知其所以然的iOS内存管理方式 前言 从我开始学习iOS的时候,身边的朋友.网上的博客都告诉我iOS的内存管理是依靠引用计数的,然后说引用计数大于1则对象保存在内存的堆中而引用 ...

  5. 知其然,知其所以然之Java基础系列(一)

    相信大家在最初接触Java基础学习的时候,也只是跟着课本上的描述学习,知其然,不知所以然,要想成为一个Java老鸟,不仅要学会怎么用,也要知道为何这么用.在Java基础系列的博客中,我会列举一系列大家 ...

  6. 关于Android模块化我有一些话不知当讲不当讲

    关于Android模块化我有一些话不知当讲不当讲 最近公司一个项目使用了模块化设计,本人参与其中的一个小模块开发,但是整体的设计并不是我架构设计的,开发半年有余,在此记录下来我的想法. 关于Andro ...

  7. AI赋能传统行业:知其然并知其所以然 - 专访平安科技美国研究院院长韩玫

    ????点击上方蓝字星标"Robinly",获取更多重磅AI访谈 Robin.ly 是立足硅谷的视频内容平台,服务全球工程师和研究人员,通过与知名人工智能科学家.创业者.投资人和领 ...

  8. 锁,知其然知其所以然

    ​ Taken by iCola 今天,从一个小问题聊起. 假设你账户上原来有100元钱,你用微信支付100元,与此同时你女票用支付宝给你转100元零花钱,你帐户的余额有没有可能变成200元或者0元? ...

  9. Android组件化打造知乎日报系列(一)—— 项目架构搭建

    Android组件化打造第三方知乎日报系列(一)-- 项目架构搭建 本节完整代码可以前往github查看,项目地址:github.com/N0tExpectEr- Android组件化打造知乎日报系列 ...

最新文章

  1. JSP中的文件操作:数据流、File类、文件浏览、目录操作、上传下载
  2. 从决策树到xgboost(一)
  3. ie浏览器模拟器_航空飞机模拟器安卓版下载-航空飞机模拟器游戏下载
  4. Matlab的数据类型及相互转换
  5. MATLAB 工具箱傻瓜式求解 NS(Navier Stoke)方程
  6. echart 表格_市政工程表格不会填?市政工程1000个表格模板,一键即可套用
  7. 2023王道C语言训练营(线索二叉树)
  8. 【Python】断言(assert)
  9. html js设置旋转动画效果图,原生JS实现逼真的图片3D旋转效果详解
  10. 教学中计算机软件的应用,计算机软件应用类课程教学方法
  11. Windows下运行LSD-SLAM
  12. 为什么 MySQL 唯一索引会导致死锁,“有心杀贼,无力回天”?
  13. c语言拔萝卜算法,拔萝卜优秀说课稿
  14. lisp画弯箭头_在CAD中直接画箭头的命令的一个方法
  15. 关于Context的理解(转)
  16. Godaddy 主机域名的购买、注册和使用
  17. Java语言学习之类加载机制与反射
  18. [矩阵论]Jordan标准形中Jordan块阶数与个数的确定
  19. 常见文件传输协议(ftp、tftp、scp)及其特点
  20. Python爬取图片实例

热门文章

  1. 华为ac控制器web配置手册_31、堂堂华为企业级AP怎么还不如家用TP的速度”快“呢?(优化篇)...
  2. 计算机在职研究生科目,计算机在职研究生考试科目都有哪些?考试难吗
  3. 【转】人家在美国怎么过的,7年,我无比惭愧
  4. Win10系统,使用VSCode提示错误fatal: detected dubious ownership in repository at
  5. Super-Resolution Mapping of Impervious Surfaces from Remotely Sensed Imagery with Points-of-Interest
  6. 黑白双轨棋·改编(定子棋,终盘换子,在计算赢子的多少)
  7. layui table 获取单元格总是多一个
  8. 【游戏逆向】CS1.6无后坐力基址寻找
  9. uniapp对接微信公众号H5微信支付、分享、小程序隐藏右上角分享胶囊
  10. 文末福利|使用Python转换PDF,Word/Excel/PPT/md/HTML都能转!