用BitmapFactory.decodeStream()方法计算图片采样率的问题

在学习图片加载时图片很大的时候直接加载到内存或则直接原图绘制到 ImageView中会导致OOM问题;

解决思路是利用BitmapFactory的系列方法中有个带有 BitmapFactory.Options 参数的方法,这个参数可以通过设置

options.inSampleSize = Int ;  //设置图片采样率
这个图片采样率的意思就是几个像素点取一个,例如原图的像素为 1000 * 1000  设置采样率为2
那么就是在横向和纵向上每2个像素点采样一次 最终得到一个 (1000/2)*(1000/2)的bitmap图片加载到内存中,从而达到占用内存变小的目的;
但是我们获取的图片的大小是不确定的,我们的控件尺寸在不同分辨率的设备上大小也不一样;所以我们不能给一个固定的采样率,需要根据获取的图片大小和控件大小来决定采样率;

计算采样率:
options.inJustDecodeBounds =true
options中该属性设置为true时,调用BitmapFactory的带options的系列方法,bitmap不会载入内存,方法返回null。但是方法会把bitmap的一些信息写入options,这样就可以在不消耗大量内存的情况下得到图片资源的尺寸信息;
这里以 BitmapFactory.decodeStream(inputStream,null,options)  方法为例:

val options = BitmapFactory.Options()
options.inJustDecodeBounds =true
BitmapFactory.decodeStream(inputStream,null,options)
//得到控件的宽高尺寸
val viewWidth =binding.imageView.measuredWidth
val viewHeight =binding.imageView.measuredHeight
//利用options中的outWidth 和 outHeight 参数和控件尺寸计算横向和纵向的最合适采样率
val x:Float = (options.outWidth/viewWidth).toFloat()
val y:Float = (options.outHeight/viewHeight).toFloat()
//下面这个是计算采样率的策略,可以有不同的策略;

//如果控件的宽高至少有一个是小于图片的宽高的时候才进行采样率计算
if ( viewHeight<options.outHeight || viewWidth<options.outWidth ) {
    // 取较大的那个作为采样率,也有的策略取小的那个 
     val s = if (x<y)  y  else  x
     options.inSampleSize = s.toInt(); //设置图片采样率
     Log.e("qqq",""+ s +">>>>>>>" + s.toInt())
}

设置好采样率后,重新把 options.inJustDecodeBounds = false 然后就可以采用上面的采样率加载图片到控件中了
val bitmap = BitmapFactory.decodeStream(inputStream,null,options)
imageView.setImageResource(bitmap)

这时候我以为大功告成了,结果发现 
图片竟然并没有显示出来,但是也并没有什么报错,第二次调用BitmapFactory.decodeStream(inputStream,null,options) 即时设置  options.inJustDecodeBounds = false 
返回的值还是null;怎么回事呢?查了一下说是我们需要在两次decode 之间将流重置  inputStream.reset() 一下 

好了加上这句在运行一下看看结果:

java.io.IOException: mark/reset not supported at java.io.InputStream.reset

???啥意思,大概就是说不支持 InputStream.reset,查了下,网上有朋友说是因为

当给定的流不支持mark和reset就会报这个错误,解决方案是用BufferedInputStream把原来的流包一层.什么时候会出现这种错误呢?获取到一个网络流,这个网络流不允许读写头来回移动,也就不允许mark/reset机制.
BufferedInputStream zipTest=new BufferedInputStream(zip);

说的很明白了,于是我们就回到上面的获取到inputStream的代码,按他说的包装一下:
val bufferedInputStream = new BufferedInputStream(inputStream);
然后在 BitmapFactory.decodeStream(bufferedInputStream,null,options) 中传入 bufferedInputStream;
val bitmap = BitmapFactory.decodeStream(inputStream,null,options)
这下总该可以了吧!!!

结果还是报错啦!!!

Caused by: java.io.IOException: Resetting to invalid mark

        at java.io.BufferedInputStream.reset(BufferedInputStream.java:450)

黑人问号脸????这个又是咋回事? 重置无效的标记?由于之间完全不了解这块的东西于是又搜了下资料,发现有篇相关的博客下的评论是这样的:

* The maximum read ahead allowed after a call to the      * <code>mark</code> method before subsequent calls to the      * <code>reset</code> method fail.      * Whenever the difference between <code>pos</code>     * and <code>markpos</code> exceeds <code>marklimit</code>,     * then the  mark may be dropped by setting     * <code>markpos</code> to <code>-1</code>.
mark(0)时没有意思的通俗的说如果你mark(1000)之后再读取1001个自己(超过1000),你就无法reset了,就是你上面的错误。很好理解:你想重新获取最近的1000个字节,可以你已经读取的自己超过了它,你当然无法回到开始的位置了。你在这里设置mark(0),之后只要调用了read方法就不能reset了!

咦?好像是在说这个reset()调用之间要用mark(Int)标记一下读取的字节size,读取超过了reset()就回不去了;
于是我又在包装bufferedInputStream后面加了一句:
bufferedInputStream.mark(1024*1024) //考虑到我读的是一个图片流应该不小把,字节给足!!!
最后再跑一下,终于成功加载出来了图片,而且也确实做到了动态计算采样率!

好了总算整明白怎么回事了!记录一下。

然后查资料的过程中还顺便了解到长图分区域加载,大概是这个类:BitmapRegionDecoder
具体看这篇大神的博客 https://www.jianshu.com/p/f576fd7313da

BitmapRegionDecoder

它提供了一系列静态方法构造实例

拿到实例后 通过 #decodeRegion() 方法,传入一个 Rect 和 一个BitmapFactory.Options 参数 即可解码出一张我们要的图片解码区域就是我们 Rect 指定的范围,拿到 Bitmap 后当然可以为所欲为了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容