概括
上一篇文章提到进行适配的时候,理想的情况是:针对不同密度的设备由设计提供不同的尺寸的图片,分别放进mipmap的不同文件夹下,以实现基本一致的用户体验。但是实际情况是多数公司都只会根据交互图出一份切图,我目前的项目也是这样。那么,此时问题就来了,图片究竟该放在mipmap-mdpi、mipmap-ndpi、mipmap-xndpi、mipmap-xxndpi、mipmap-xxxndpi中的那个文件夹下呢?
之前我是直接把图片放在mipmap-hdpi的文件夹下,不同的设备会自动去到该文件夹下找相应的图片,但是最近突然发现这样做存在非常大的问题,因为:相同的图片放在不同的文件夹下在不同的设备上的内存占用是不同的。
具体我们来分析一下:
准备的图片参数为:1080*520px、32-bit color、文件大小373.32KB的图片,手机是魅蓝note2,1920*1080px,尺寸为5.5英寸,设备像素密度为3。
主界面
public class MainActivity extends AppCompatActivity {
private ImageView imgView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgView = (ImageView) findViewById(R.id.img);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
printImgWH();
}
private void printImgWH(){
if (imgView==null){
return;
}
Drawable drawable = imgView.getDrawable();
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
System.out.println("img width:"+bitmap.getWidth()+";img height:"+bitmap.getHeight());
}
}
布局界面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.zhl.mipmapdemo.MainActivity">
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_group_bg"/>
</LinearLayout>
代码比较简单。我们分以下的几种情况进行验证:
情况1.首先不设置图片,运行app后内存占用、图片宽高:
结果:app占用内存为17.47M,空闲内存为2.67M.图片(bitmap)宽高输出0*0px
情况2.把图片放在mipmap-xxhdpi文件夹下,运行app后内存占用、图片宽高:
结果:app占用内存为19.61M,空闲内存为0.52M.图片(bitmap)宽高输出1080*520px
情况3.把图片放在mipmap-mdpi文件夹下,运行app后内存占用、图片宽高:
结果:app占用内存为36.43M,空闲内存为3.71M.图片(bitmap)宽高输出3240*1560px
情况4.把图片放在mipmap-hdpi、mipmap-xhdpi、mipmap-xxxhdpi文件夹下,运行app后内存占用、图片宽高:
这三种情况我就不放截图了。结果分别是:
hdpi:app占用内存为25.72M,空闲内存为3.71M.图片宽高(bitmap)输出2160*1040px
xhdpi:app占用内存为21.96M,空闲内存为3.77M.图片宽高(bitmap)输出1620*780px
xxxhdpi:app占用内存为18.68M,空闲内存为1.45M.图片宽高(bitmap)输出810*390px
情况5.把图片放在mipmap-xxhdpi文件夹下,同时设置ImageView的宽高分别为1dp,运行app后内存占用、图片宽高:
结果:app占用内存为19.83M,空闲内存为0.30M.图片(bitmap)宽高输出3240*1560px,图片(ImageView)的宽高输出为3*3px
注:此种情况是后面加的,因此与情况2的结果有差异,理想情况下app占用内存和空闲内存应与情况2一致。
结果分析:
1、在同一设备上,相同的图片放在设备像素密度越高的文件夹下,图片输出的宽高越小。且根据像素密度的关系,呈现3:2:1.5:1:0.75的比例关系。
2、在同一设备上,相同的图片放在设备像素密度越高的文件夹下,图片占用的内存越小。
3、图片在硬盘上的大小,与之在内存中的大小没有直接关系。
4、相同像素密度的文件夹下,图片加载到内存中bitmap的大小不受ImageView大小的影响,即图片占用的内存大小与图片(ImageView)的实际大小无关,只与图片的分辨率有关。
我们以xxhdpi设备上得到的数据进行计算内存占用:
1080*520*4*8/8 byte,即2246.4kb,即2.2464M.所以理论计算此时占用的内容更应该为初始内存加此内存,即17.47+2.2464=19.7164,与实际值19.61基本一致(在误差范围内)。
Android中bitmap的默认Config为:ARGB_8888,此config表示每个像素分别由A、R、G、B四个通道表示,每个通道占8bit,所以每一个像素的大小为4*8=32bit,即4kb。
另外,图片在硬盘存储时表现的大小,与内存中表现不一致。看到另外一篇博文(结尾附上)解释的很详细,是这样写的:
存放在硬盘上的图片文件,会根据各自的压缩规则进行压缩,比如Jpeg这种有损压缩的图片格式,最常使用可变字长编码的哈弗曼编码,会使用哈弗曼树,也就是最优二叉树,根据某些数据出现的频率对数据段编码,从而减少占用的硬盘大小。
比如说“10111”这个序列在图片的二进制数据中出现的概率最大,那我们可以用“01”来代替这一段数据,原来5位的数据,用2位就可以表示了,这就是压缩率60%。当然这只是打个比方,在实际操作中需要考虑“异前缀原则”等编码的基本原则。
而如果把图像读取到内存中就不一样了,因为我们需要每一个像素都能在屏幕上显示,所以会把每个像素点都加载至内存中,不会对相同像素进行压缩或者是替换,所以你也应该能明白前面提到的Bitmap占用内存大小的计算公式的由来了。
结论
<p>Android设备加载图片时,它会首先去到与该设备像素密度一致的文件夹下找图片,如果找不到,他会依次到其他文件夹下去找,但是最终的显示结果会进行缩放,规则是根据设备像素密度和对应文件夹的像素密度的反比缩放图片。即当访问的文件夹的代表像素密度更大时,图片表现出来的宽高会更小,内存占用也会更小。</p>
所以,当我们只能为app提供一套切图时,优先的考虑是尽可能的提供最高设备像素密度下的切图,并放在相应的文件夹下,即xxxhdpi设备下的切图,其次考虑提供主流像素密度设备的切图,目前我项目中提供的均为1920*1080px,xxhdpi下的切图。这种切图在xxxhdpi设备下会进行一定的缩小显示,需要注意最终呈现效果。
</br>
参考链接:
http://blog.csdn.net/zhaokaiqiang1992/article/details/49787117