Android面试收集录15 Android Bitmap压缩策略
一、为什么Bitmap需要高效加载?
现在的高清大图,动辄就要好几M,而Android对单个应用所施加的内存限制,只有小几十M,如16M,这导致加载Bitmap的时候很容易出现内存溢出。如下异常信息,便是在开发中经常需要的:
java.lang.OutofMemoryError:bitmap size exceeds VM budget
为了解决这个问题,就出现了Bitmap的高效加载策略。其实核心思想很简单。假设通过ImageView来显示图片,很多时候ImageView并没有原始图片的尺寸那么大,这个时候把整个图片加载进来后再设置给ImageView,显然是没有必要的,因为ImageView根本没办法显示原始图片。这时候就可以按一定的采样率来将图片缩小后再加载进来,这样图片既能在ImageView显示出来,又能降低内存占用从而在一定程度上避免OOM,提高了Bitmap加载时的性能。
二、Bitmap高效加载的具体方式
1.加载Bitmap的方式
Bitmap在Android中指的是一张图片。通过BitmapFactory类提供的四类方法:decodeFile,decodeResource,decodeStream和decodeByteArray,分别从文件系统,资源,输入流和字节数组中加载出一个Bitmap对象,其中decodeFile,decodeResource又间接调用了decodeStream方法,这四类方法最终是在Android的底层实现的,对应着BitmapFactory类的几个native方法。
2.BitmapFactory.Options的参数
①inSampleSize参数
上述四类方法都支持BitmapFactory.Options参数,而Bitmap的按一定采样率进行缩放就是通过BitmapFactory.Options参数实现的,主要用到了inSampleSize参数,即采样率。通过对inSampleSize的设置,对图片的像素的高和宽进行缩放。
当inSampleSize=1,即采样后的图片大小为图片的原始大小。小于1,也按照1来计算。 当inSampleSize>1,即采样后的图片将会缩小,缩放比例为1/(inSampleSize的二次方)。
例如:一张1024 ×1024像素的图片,采用ARGB8888格式存储,那么内存大小1024×1024×4=4M。如果inSampleSize=2,那么采样后的图片内存大小:512×512×4=1M。
注意:官方文档支出,inSampleSize的取值应该总是2的指数,如1,2,4,8等。如果外界传入的inSampleSize的值不为2的指数,那么系统会向下取整并选择一个最接近2的指数来代替。比如3,系统会选择2来代替。当时经验证明并非在所有Android版本上都成立。
关于inSampleSize取值的注意事项: 通常是根据图片宽高实际的大小/需要的宽高大小,分别计算出宽和高的缩放比。但应该取其中最小的缩放比,避免缩放图片太小,到达指定控件中不能铺满,需要拉伸从而导致模糊。
例如:ImageView的大小是100×100像素,而图片的原始大小为200×300,那么宽的缩放比是2,高的缩放比是3。如果最终inSampleSize=2,那么缩放后的图片大小100×150,仍然合适ImageView。如果inSampleSize=3,那么缩放后的图片大小小于ImageView所期望的大小,这样图片就会被拉伸而导致模糊。
②inJustDecodeBounds参数
我们需要获取加载的图片的宽高信息,然后交给inSampleSize参数选择缩放比缩放。那么如何能先不加载图片却能获得图片的宽高信息,通过inJustDecodeBounds=true,然后加载图片就可以实现只解析图片的宽高信息,并不会真正的加载图片,所以这个操作是轻量级的。当获取了宽高信息,计算出缩放比后,然后在将inJustDecodeBounds=false,再重新加载图片,就可以加载缩放后的图片。
注意:BitmapFactory获取的图片宽高信息和图片的位置以及程序运行的设备有关,比如同一张图片放在不同的drawable目录下或者程序运行在不同屏幕密度的设备上,都可能导致BitmapFactory获取到不同的结果,和Android的资源加载机制有关。
3.高效加载Bitmap的流程
①将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片。
②从BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidth和outHeight参数。
③根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize。
④将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。
三、Bitmap高效加载的代码实现
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//加载图片
BitmapFactory.decodeResource(res,resId,options);
//计算缩放比
options.inSampleSize = calculateInSampleSize(options,reqHeight,reqWidth);
//重新加载图片
options.inJustDecodeBounds =false;
return BitmapFactory.decodeResource(res,resId,options);
} private static int calculateInSampleSize(BitmapFactory.Options options, int reqHeight, int reqWidth) {
int height = options.outHeight;
int width = options.outWidth;
int inSampleSize = 1;
if(height>reqHeight||width>reqWidth){
int halfHeight = height/2;
int halfWidth = width/2;
//计算缩放比,是2的指数
while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
} return inSampleSize;
}
这个时候就可以通过如下方式高效加载图片:
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.mipmap.ic_launcher,100,100);
除了BitmapFactory的decodeResource方法,其他方法也可以类似实现。
四、参考文章
https://github.com/LRH1993/android_interview/blob/master/android/basis/bitmap.md
Android面试收集录15 Android Bitmap压缩策略的更多相关文章
- Android面试收集录13 Android虚拟机及编译过程
一.什么是Dalvik虚拟机 Dalvik是Google公司自己设计用于Android平台的Java虚拟机,它是Android平台的重要组成部分,支持dex格式(Dalvik Executable)的 ...
- Android面试收集录16 Android动画总结
一.Android 动画分类 总的来说,Android动画可以分为两类,最初的传统动画和Android3.0 之后出现的属性动画: 传统动画又包括 帧动画(Frame Animation)和补间动画( ...
- Android面试收集录18 Android Context详解
Activity mActivity =new Activity() 作为Android开发者,不知道你有没有思考过这个问题,Activity可以new吗?Android的应用程序开发采用JAVA语言 ...
- Android面试收集录17 Android进程优先级
在安卓系统中:当系统内存不足时,Android系统将根据进程的优先级选择杀死一些不太重要的进程,优先级低的先杀死.进程优先级从高到低如下. 前台进程 处于正在与用户交互的activity 与前台act ...
- Android面试收集录14 Android进程间通信方式
一.使用 Intent Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间 ...
- Android面试收集录 Android系统的资源+其他
1.Android应用程序的资源是如何存储的,如何使用? res文件夹或者assets文件夹 res目录中的资源在R类中生成一个int变量,然后再布局文件中可以直接使用,在代码中,要getResour ...
- Android面试收集录 电话、短信和联系人、多媒体技术
1.请写出调用系统拨号界面? Intent intent=new Intent(Intent.ACTION_DIAL,Uri.pase("tel:12345678910")); s ...
- Android面试收集录 Android布局
1.请说出Android中的五种布局,并介绍作用? FrameLayout(堆栈布局),层叠方式显示,类似于PhotoShop上的层叠图层. LinearLayout(线性布局),将视图以水平或者垂直 ...
- Android面试收集录11 Window+Activity+DecorView+ViewRoot之间的关系
一.职能简介 Activity Activity并不负责视图控制,它只是控制生命周期和处理事件.真正控制视图的是Window.一个Activity包含了一个Window,Window才是真正代表一个窗 ...
随机推荐
- 在Git上创建新分支(实用性高!!!)
在github上创建仓库: Create a new repository on the command line touch README.md git init git add README.md ...
- Android Studio 小技巧(1):如何导入AAR文件
1. 导入AAR.JAR文件 File- > New -> New Module 这样子AAR文件就导入了,然后在app中的build.gradle中做如下添加 dependencies ...
- ArcGIS几种数据格式2
各种数据的组织形式不一样,其中shp.Coverage.Raster.CAD为文件类型,Geodatabase为空间数据库.Workstaion常用Coverage数据格式.现在ESRI公司推荐使用G ...
- 通过adb获取应用的Activity堆栈信息
获取所用应用 adb shell dumpsys activity 获取自己的应用 adb shell dumpsys activity | grep 应用的package 获取处于栈顶的activi ...
- lrzsz的使用
可以方便的在本地PC机和远程服务器之间传输文件. 1.下载 直接在centos上执行命令yum -y install lrzsz 2.上传文件 rz // 上传文件,执行命令rz,会跳出文件选择窗口, ...
- 【[TJOI2018]异或】
写板子了,可持久化\(Trie\)的板子了 其实和主席树写法类似,还是存好左右儿子之后存好权值 之后差分去查询就好了 这道题第一问我们直接\(dfs\)序转化成区间 第二问搞成\(x,y,lca(x, ...
- 【转】DataURL在Web浏览器中的兼容性总结
IE8+,Firefox,Chrome,Opera,Safari 等现代浏览器普遍支持data URL IE8 data URL 最大长度限制为32k字节,超出无效.IE9+没有这个限制 IE只识别b ...
- Ubuntu 12.04 the system is running in low-graphics mode
1.出现问题如图所示: 2.解决方案: Ctrl + Alt + F1 df -h 输入密码,到了这一步,也是可以使用terminal,那么没有图形界面也是可以的 cd /etc/X11 sudo c ...
- phpMyAdmin提示找不到mcrypt和mbstring模块
yum install php-mcryptyum install php-mbstringphp -m 查看是否安装成功 service httpd restart 重启服务器 注: 这里可能会出现 ...
- 11_1_GUI
11_1_GUI 1. AWT AWT(Abstract Window Toolkit)包括了很多类和接口,用于Java Application的GUI(Graphics User Interface ...