solarsaber 2015-07-24
LoadingLargeBitmapsEfficiently
有效加载大型Bitmap
Imagescomeinallshapesandsizes.
图片的形状和尺寸是各种各样的
Inmanycasestheyarelargerthanrequiredforatypicalapplicationuserinterface(UI).
Forexample,thesystemGalleryapplicationdisplaysphotostakenusingyourAndroiddevices'scamerawhicharetypicallymuchhigherresolutionthanthescreendensityofyourdevice.
很多情况中,他们都大于典型应用UI所需要的
例如,系统Gallery应用显示使用你的Android设备的camera拍出的照片,它们分辨率都远大于你的设备的屏幕密度
Giventhatyouareworkingwithlimitedmemory,ideallyyouonlywanttoloadalowerresolutionversioninmemory.
ThelowerresolutionversionshouldmatchthesizeoftheUIcomponentthatdisplaysit.
Animagewithahigherresolutiondoesnotprovideanyvisiblebenefit,butstilltakesuppreciousmemoryandincursadditionalperformanceoverheadduetoadditionalontheflyscaling.
由于你在一个有限内存的设备上工作,理想情况下,你只是想在内存中加载一个低分辨率的版本
这个低分辨率的版本应该正好与显示它的UI组件大小匹配
一个高分辨率的图像不会提供任何可见的好处,但是仍然会占据非常大的内存并且会引起额外的性能开销
Thislessonwalksyouthroughdecodinglargebitmapswithoutexceedingtheperapplicationmemorylimitbyloadingasmallersubsampledversioninmemory.
这一课将给你展示通过在内存中加载一个小一些的子样版本来解码大bitmap而不会超过每一个应用内存限制
ReadBitmapDimensionsandType
读取bitmap尺寸与类型
TheBitmapFactoryclassprovidesseveraldecodingmethods(decodeByteArray(),decodeFile(),decodeResource(),etc.)forcreatingaBitmapfromvarioussources.
Choosethemostappropriatedecodemethodbasedonyourimagedatasource.
ThesemethodsattempttoallocatememoryfortheconstructedbitmapandthereforecaneasilyresultinanOutOfMemoryexception.
EachtypeofdecodemethodhasadditionalsignaturesthatletyouspecifydecodingoptionsviatheBitmapFactory.Optionsclass.
SettingtheinJustDecodeBoundspropertytotruewhiledecodingavoidsmemoryallocation,returningnullforthebitmapobjectbutsettingoutWidth,outHeightandoutMimeType.Thistechniqueallowsyoutoreadthedimensionsandtypeoftheimagedatapriortoconstruction(andmemoryallocation)ofthebitmap.
BitmapFactory类提供几种解码方法(decodeByteArray(),decodeFile(),decodeResource()等等)从资源中来建立一个bitmap
基于你的图片数据源选字最恰当的解码方式
这些方法试图为构建bitmap分配内存,因而可能很容易的导致一个OutOfMemory异常
每种解码方法都有额外的签名好让你通过BitmapFactory.Options指定解码选项
当解码的时候,设置属性inJustDecodeBounds=true来避免内存分配,返回值为null,但是设置了BitmapFactory.Options的outWidth,outHeight和outMimeType属性。
这种技术允许你在建立bitmap之前,读取图片数据的尺寸和类型
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.id.myimage, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType;
Toavoidjava.lang.OutOfMemoryexceptions,checkthedimensionsofabitmapbeforedecodingit,unlessyouabsolutelytrustthesourcetoprovideyouwithpredictablysizedimagedatathatcomfortablyfitswithintheavailablememory.
为了避免java.lang.OutOfMemory异常,在解码bitmap之前检查他的尺寸,除非你绝对信任提供给你的图片资源的大小是可知的,正好在可用内存范围内的
LoadaScaledDownVersionintoMemory
加载一个按比例比缩小版本的到内存中
Nowthattheimagedimensionsareknown,theycanbeusedtodecideifthefullimageshouldbeloadedintomemoryorifasubsampledversionshouldbeloadedinstead.
Herearesomefactorstoconsider:
现在图片尺寸知道了,它们可以用于判断全图是否应该被加载到内存,或者应该加载子样版本
下面一些因素需要考虑:
Estimatedmemoryusageofloadingthefullimageinmemory.
Amountofmemoryyouarewillingtocommittoloadingthisimagegivenanyothermemoryrequirementsofyourapplication.
DimensionsofthetargetImageVieworUIcomponentthattheimageistobeloadedinto.
Screensizeanddensityofthecurrentdevice.
Forexample,it’snotworthloadinga1024x768pixelimageintomemoryifitwilleventuallybedisplayedina128x96pixelthumbnailinanImageView.
评估加载全图所使用的内存
基于你的应用中任何其他内存需求,加载这个图片你愿意提供多少内存
图片要加载到的目标ImageView或者其他UI组件的尺寸
当前设备的屏幕尺寸和密度
例如,如果图片最终是要在一个ImageView中显示为一个128x96像素的缩略图,那么加载一张1024x768像素图片到内存中是不值得的
Totellthedecodertosubsampletheimage,loadingasmallerversionintomemory,setinSampleSizetotrueinyourBitmapFactory.Optionsobject.
Forexample,animagewithresolution2048x1536thatisdecodedwithaninSampleSizeof4producesabitmapofapproximately512x384.
Loadingthisintomemoryuses0.75MBratherthan12MBforthefullimage(assumingabitmapconfigurationofARGB_8888).
Here’samethodtocalculateathesamplesizevaluebasedonatargetwidthandheight:
在你的BitmapFactory.Options对象中设置inSampleSize=true,可以告诉解码器把图片子样话,加载一个小版本到内存中
例如,一个分辨率为2048x1536的图片,使用inSampleSize=4进行编码,产出的bitmap大致为512x384.
一个基于目标宽高的方法来计算样本大小的值
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // Calculate ratios of height and width to requested height and width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // Choose the smallest ratio as inSampleSize value, this will guarantee // a final image with both dimensions larger than or equal to the // requested height and width. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; }
Note:Usingpowersof2forinSampleSizevaluesisfasterandmoreefficientforthedecoder.
However,ifyouplantocachetheresizedversionsinmemoryorondisk,it’susuallystillworthdecodingtothemostappropriateimagedimensionstosavespace.
注意:解码时使用2的方幂设置inSampleSize,速度和效率都更好一些
然而,如果你打算缓存调整过的图片版本到内存或者到磁盘中,通常还是需要解码到最合适的图片大小来节省空间
Tousethismethod,firstdecodewithinJustDecodeBoundssettotrue,passtheoptionsthroughandthendecodeagainusingthenewinSampleSizevalueandinJustDecodeBoundssettofalse:
使用这个方法,首先设置inJustDecodeBounds=true来解码,传递这个选项,然后使用新的inSampleSize值并且设置inJustDecodeBounds=false再解码一次
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
ThismethodmakesiteasytoloadabitmapofarbitrarilylargesizeintoanImageViewthatdisplaysa100x100pixelthumbnail,asshowninthefollowingexamplecode:
这个方法使得加载一个任意大小的图片到一个ImageView显示一个100x100像素的缩略图变得容易,就像下面示例代码展示的那样
mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Youcanfollowasimilarprocesstodecodebitmapsfromothersources,bysubstitutingtheappropriateBitmapFactory.decode*methodasneeded.
根据需要通过替换恰当的BitmapFactory.decode*方法,你可以效仿出一个简单的过程来从其他资源中解码bitmap
原文地址如下,英文水平实在有限,希望拍砖同时能给予指正。
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html