志在指尖
用双手敲打未来

android学问:如何高效加载大图

前言
最近线上有用户反应在App运用过程中遇到大图的时,App异常的卡顿,以至会呈现解体的状况。后来排查了一番,发现一个同事在处置图片时,直接原图加载没有做任何“紧缩”。这个case的呈现,也就引出了这篇文章的必要性。
我们日常开发过程中,都会运用各种各样的图片库比方Glide。由于一切图片操作都是一股脑的交给图片库去处置,所以即便在遇到大图加载的时分,也无法“复现”这类问题。
由于主流的图片库都帮我们对大图停止了处置(正印证了那句话:当你能轻松进去的时分,你就该明白,不是你凶猛,只是有人在前面替你开路——“鲁讯”)。
既然话都说开了,我们作为新时期下的福报程序员,那就必需要在这条路上探探深浅。其实图片紧缩的方式有很多种,今天我们只需一种,那就是Google原生的高效加载大图的计划。
正文
停止紧缩之前,我们先来感受一下不紧缩会怎样…
一、不紧缩,直接加载大图
我随意new了一下项目,搞了一个这样的图:
其实也不是特别大,就是一张1080P的图。
然后随意的用一个ImageView去加载一下:
iv.setImageResource(R.drawable.test)
当我尝试run的时分,我高估了我的测试机….没有加载出来,就直接崩了。Logcat也是够直接,无情吐槽:
这么一张图,一共需求132710400Bytes的内存,也就是132m….等等,不对?!分辨率1080*1920的图片怎样可能会运用100+m的内存?
我们都晓得,正常一个图片被加载到内存里的文件大小=图片分辨率的宽*图片分辨率的高*颜色格式。带入这个公式内存大小=1080*1920*4=7.9m,绝不可能是100+m这么多!
这里可能有朋友会有疑问,为啥JPEG的格式会4,JPEG格式没有alpha通道,不应该占这么大的空间。其实详细几,还是需求看这张图最终Bitmap.Config解出来的值,我这张图解出来是ARGB_8888,所以还是要*4。
假如你也有这个疑问,那么接下来的内容你要好美观咯。这个学问点恐怕是盲区…
二、番外:drawble、drawble-xxhdpi有什么区别
作为一个番外的内容局部。这一章节其实和图片紧缩没有什么关系,只是额外聊一聊drawble这个文件夹
上述问题的基本缘由就是在于文件放置的位置,我只在drawble文件夹下放置了图片资源。
所以…这种case下,假如加载这个资源的手机是一个高密度屏幕,那么这张图片被展现时,并非1080*1920…
接下来我们来看一看,为什么资源文件随意放会带来这么大的问题!(以下内容,局部来自于官方文档)
文档中提到,假如资源提供不当,会招致缩放失真…。这里为什么系统要停止缩放其实也很好了解:
关于系统来说,假如它向下(低密度)才找到需求援用的资源文件,那么最佳的战略便是将找到的图片资源整体放大。由于那里的图,预期是给低分辨率手机准备的。那么同理,假如系统向上(高密度)找到了需求援用的资源文件,那么减少无疑是最佳的选择。由于那里的图,预期是给高分辨率手机准备的。所以基于此,上述中OOM的内存值132710400bytes是这么算出来的:1080*4(这个4是手机dpi640/资源dpi160所得)*1920*4*4
小贴士:dpi=手机分辨率长宽各自平方之和开方,除以对角线长度(单位英寸)。当然我们也能够经过api:resources.displayMetrics.xdpi。这里得到的值就根本等于当前手机的dpi
所以,强迫加载这么大的一张图,是不是不担任任!这么大,硬往里塞,搁谁谁受得了?
三、Google提供的处理计划
既然我们曾经明白硬来是不行了,所以还是要采取一些技巧的。文章中开篇就道出了问题的所在:
Imagescomeinallshapesandsizes.Inmanycasestheyarelargerthanrequiredforatypicalapplicationuserinterface(UI).Forexample,thesystemGalleryapplicationdisplaysphotostakenusingyourAndroiddevices’scamerawhicharetypicallymuchhigherresolutionthanthescreendensityofyourdevice.
Giventhatyouareworkingwithlimitedmemory,ideallyyouonlywanttoloadalowerresolutionversioninmemory.ThelowerresolutionversionshouldmatchthesizeoftheUIcomponentthatdisplaysit.Animagewithahigherresolutiondoesnotprovideanyvisiblebenefit,butstilltakesuppreciousmemoryandincursadditionalperformanceoverheadduetoadditionalontheflyscaling.
简单翻译一下就是:太大就不要硬塞,缩到适宜的尺寸再塞
文档里还有比拟有意义的一句话:Thereareseverallibrariesthatfollowbestpracticesforloadingimages.Youcanusetheselibrariesinyourapptoloadimagesinthemostoptimizedmanner.WerecommendtheGlide
官方引荐,最为致命
其实文档中直接贴出了能够Ctrl+C/V就能运用的代码:
imageView.setImageBitmap(decodeSampledBitmapFromResource(resources,R.id.myimage,100,100))funcalculateInSampleSize(options:BitmapFactory.Options,reqWidth:Int,reqHeight:Int):Int{//Rawheightandwidthofimageval(height:Int,width:Int)=options.run{outHeighttooutWidth}varinSampleSize=1if(height>reqHeight||width>reqWidth){valhalfHeight:Int=height/2valhalfWidth:Int=width/2//CalculatethelargestinSampleSizevaluethatisapowerof2andkeepsboth//heightandwidthlargerthantherequestedheightandwidth.while(halfHeight/inSampleSize>=reqHeight&&halfWidth/inSampleSize>=reqWidth){inSampleSize*=2}}returninSampleSize}fundecodeSampledBitmapFromResource(res:Resources,resId:Int,reqWidth:Int,reqHeight:Int):Bitmap{//FirstdecodewithinJustDecodeBounds=truetocheckdimensionsreturnBitmapFactory.Options().run{inJustDecodeBounds=trueBitmapFactory.decodeResource(res,resId,this)//CalculateinSampleSizeinSampleSize=calculateInSampleSize(this,reqWidth,reqHeight)//DecodebitmapwithinSampleSizesetinJustDecodeBounds=falseBitmapFactory.decodeResource(res,resId,this)}}
代码很好了解,就是将需求加载的图片,按目的所需的加载尺寸停止一次采样,经过采样的值停止等比缩放。
不过这里有一个有趣的细节:官方的代码里是将采样结果停止了*2(
inSampleSize*=2
)。当时经过实战我们会发现,
inSampleSize
并不一定要传2的幂,传3传5传其他也是有效果的。
文档中提到这么一句话:
Note:Apoweroftwovalueiscalculatedbecausethedecoderusesafinalvaluebyroundingdowntothenearestpoweroftwo,aspertheinSampleSizedocumentation.(以2的幂作为计算结果,是依据inSampleSize文档,解码器经过四舍五入到最接近的2的幂来运用最终值。)
依照文档的解释inSampleSize为2/3时,效果一样,毕竟3最接近2的幂的值还是2。当时势实跑起来会发现,2和3的结果并不一样:
当inSampleSize=3时,图片长和宽就是比减少了3倍…所以真是不晓得官网的葫芦里卖的什么药。

未经允许不得转载:IT技术网站 » android学问:如何高效加载大图
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载