关于Android中图片大小、内存占用与drawable文件夹关系的研究与分析

从上一篇文章《Android屏幕适配全攻略》写完之后,经常会有朋友问我这个问题:“能不能一个App只提供一套切图适应所有的分辨率呢?”我觉得有必要写一篇文章来研究一下这个问题,所以就有了这篇文章。

    • 研究内容
    • 研究方法
    • 测试环境
    • 研究过程
    • 结果分析
    • 结论
    • 另外一个难以解释的问题

研究内容

本篇内容主要探讨以下场景:同一张图片,放置在不同的drawable文件夹,在同一设备上运行,对图片大小及内存占用有什么影响。

研究方法

  • 控制变量法
  • 分析法

测试环境

采用锤子T1手机(1080*1960,xxhdpi)进行测试

对于内存的查看,使用AS自带的内存查看工具。

图片大小使用如下代码获取

 private void printBitmapSize(ImageView imageView) {
        Drawable drawable = imageView.getDrawable();
        if (drawable != null) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            Bitmap bitmap = bitmapDrawable.getBitmap();
            Log.d(TAG, " width = " + bitmap.getWidth() + " height = " + bitmap.getHeight());
        } else {
            Log.d(TAG, "Drawable is null !");
        }
    }

研究过程

下面将给出测试的过程的截图,然后进行分析和总结

下面的测试使用的是一张720*1280分辨率的png图片,32位色,占用硬盘大小为77.11k

下面给出测试工程代码,非常简单

主界面

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private ImageView img;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        img = (ImageView) findViewById(R.id.img);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        printBitmapSize(img);
    }

    private void printBitmapSize(ImageView imageView) {
        Drawable drawable = imageView.getDrawable();
        if (drawable != null) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            Bitmap bitmap = bitmapDrawable.getBitmap();
            Log.d(TAG, " width = " + bitmap.getWidth() + " height = " + bitmap.getHeight());
        } else {
            Log.d(TAG, "Drawable is null !");
        }
    }

}

布局界面

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

在不设置图片的情况下,App占用内存8.31M

以下为测试部分

把图片放置在drawable文件夹下,图片大小为2160*3840,内存占用39.88M

把图片放置在drawable文件夹下,图片大小为2160 * 3840,内存占用39.88M

把图片放置在drawable-mdpi文件夹下,图片大小为2160 * 3840,内存占用39.84M

把图片放置在drawable-hdpi文件夹下,图片大小为1440 * 2560,内存占用22.26M

把图片放置在drawable-xhdpi文件夹下,图片大小为1080 * 1920,内存占用16.11M

把图片放置在drawable-xxhdpi文件夹下,图片大小为720 * 1280,内存占用11.86M

把图片放置在drawable-xxxhdpi文件夹下,图片大小为540 * 960,内存占用10.29M

结果分析

从上面的测试结果,我们可以得出如下结论:

  1. 同一张图片,放在不同目录下,会生成不同大小的Bitmap
  2. Bitmap的长度和宽度越大,占用的内存就越大
  3. 图片在硬盘上占用的大小,与在内存中占用的大小完全不一样

下面我会对上面几个问题一一解释。

我们以放在drawable文件夹下面的图片为例,加载到内存之后,2160*3840大小的Bitmap占用的内存为

2160 * 3840 * 4 = 3317,7600 byte = 3,2400kb = 31.640625 M

所以drawable文件夹下的App内存占用 = 原始内存8.31M+图片内存31.64M= 39.95M ,与实际内存占用39.88M存在0.1755%的误差,在误差范围之内。

先简单解释一下上面的计算公式,长*宽是图片的像素总数,乘以4则是因为一个像素占用A、R、G、B四个通道,每个通道占用8位,所以描述一个像素需要32位即4个字节。

一个颜色通道需要8位描述,2^8=256,所以每个颜色通道就有256种状态。如果把彩色图转化成灰阶图的话,也有256种状态分割从白色到黑色之间的过渡颜色。

当然,也并不是所有格式的图片每个像素占用4字节,这和图片在加载时设置的Bitmap.Config有关,默认的是Bitmap.Config.ARGB_8888,其他类型如下:

  • Bitmap.Config.ALPHA_8 此时图片只有alpha值,没有RGB值,

    一1个像素占用一个字节

  • Bitmap.Config.ARGB_4444 一个像素占用2个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占4个bites共16bites,即2个字节
  • Bitmap.Config.ARGB_8888 一个像素占用4个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占8个bites,共32bites,即4个字节。这是一种高质量的图片格式,在电脑上普通采用。它也是Android手机上一个Bitmap的默认格式。
  • Bitmap.Config.RGB_565 一个像素占用2个字节,没有alpha(A)值,即不支持透明和半透明,Red(R)值占5个bites ,Green(G)值占6个bites ,Blue(B)值占5个bites,共16bites,即2个字节。对于没有透明和半透明颜色的图片来说,该格式的图片能够达到比较的呈现效果,相对于ARGB_8888来说也能减少一半的内存开销。因此它是一个不错的选择。

那么为啥在硬盘上存储只需要77.11k,放到内存里面就需要30多M呢?

因为这根本不是一回事呀~

存放在硬盘上的图片文件,会根据各自的压缩规则进行压缩,比如Jpeg这种有损压缩的图片格式,最常使用可变字长编码的哈弗曼编码,会使用哈弗曼树,也就是最优二叉树,根据某些数据出现的频率对数据段编码,从而减少占用的硬盘大小。

比如说“10111”这个序列在图片的二进制数据中出现的概率最大,那我们可以用“01”来代替这一段数据,原来5位的数据,用2位就可以表示了,这就是压缩率60%。当然这只是打个比方,在实际操作中需要考虑“异前缀原则”等编码的基本原则。

而如果把图像读取到内存中就不一样了,因为我们需要每一个像素都能在屏幕上显示,所以会把每个像素点都加载至内存中,不会对相同像素进行压缩或者是替换,所以你也应该能明白前面提到的Bitmap占用内存大小的计算公式的由来了。

说到这里,其实后两个结论已经解释清楚了,那么为什么“同一张图片,放在不同目录下,会生成不同大小的Bitmap”呢?

如果你真的看懂了我之前写的文章,那么这个问题应该不算问题。

我的测试设备为锤子T1,1080*1960,xxhdpi,所以说,如果把这张放置在xxhdpi的话,应该不会对图像进行放缩,也就是原始大小,所以我们在前面得到drawable-xxhdpi文件夹下,图片大小为720 * 1280是完全可以理解的,就是图片本身的大小。

当图片放置在drawable-hdpi中时,图片大小为1440 * 2560,长宽变为原来的两倍,这是因为不同分辨率之间的倍数关系导致的,来一张图

我们可以很明显的看到xxhdpi是hdpi的2倍,所以如果单独放置在某个drawable文件夹,手机会自动根据当前的屏幕密度对图片进行放缩。

比如上面,当把图片放置在xxxhdpi里面的时候,在xxhdpi的设备上,图片长 = 720 * (3/4) = 540,图片宽 = 1280 * (3/4) = 960,这与上面的测试结果是完全一致的。

至于为什么在前面的测试中,drawable和drawable-mdpi是一样的大小,是因为drawable-mdpi是系统默认的像素密度,其他像素密度都以它为基数,当只在drawable中存在图片时,如果使用该图片,那么将按照drawable-mdpi的放缩比例进行放缩。

结论

从上面的测试我们可以得出以下几个结论:

  1. 当图片放置在不同drawable文件夹中,且只有这一张图片时,运行设备会根据自身的屏幕密度,对图片进行放缩,放缩比例符合前面图上的规则
  2. 图片文件的大小与在内存中占用的大小没关系,内存中实际占用大小与图片分辨率、像素显示参数有关

所以,在一个App里面使用一套UI理论上应该是没有问题的,但是要注意

  • 最好使用较高分辨率的切图,并且放置在正确的drawable文件夹中,比如按照xxhdpi的分辨率进行切图,放置在drawable-xxhdpi中
  • 对于可以使用.9格式的图片,最好使用.9,减少资源大小
  • 如果有条件,最好提供多套UI切图。如果只有一套切图,系统需要对图片进行压缩,会进行大量运算,影响设备性能。同时,在某些情况下,系统对图片的压缩会可能会出现锯齿,造成信息的丢失

思考一下,如果把一个本来应该放在drawable-xxhdpi里面的图片放在了drawable文件夹中会出现什么问题呢?

在xxhdpi设备上,图片会被放大3倍,图片内存占用就会变为原来的9倍!

另外一个难以解释的问题

我还试着将上面那张图片放置在drawable-xxhdpi文件夹下,观察在不同屏幕密度设备的表现

xxhdpi设备(T1),图片大小为720 * 1280,内存占用 = 11.86M-8.31M= 3.55M

xxhdpi设备(N5) ,图片大小为720 * 1280,内存占用 = 20.19M-16.82M = 3.37M

xxhdpi设备(魅族m351),图片大小为720 * 1280,内存占用 = 20.17M-17.84M = 2.33M

xhdpi设备(华为g716-l070) 内存占用 = 7.72M-2.66M = 5.06M

hdpi设备(联想A360e),图片大小为360 * 640,内存占用 = 2.83M-2.96M = -0.7M

hdpi设备(酷派7269),图片大小为320 * 568,内存占用 = 6.85M-2.78M = 4.07M

然后我就凌乱了,这是什么玩意!除了俺的大锤子和原生的N5符合图片内存计算公式,其他的设备都是什么玩意!联想手机的还是负值!不过我在模拟机上测试得到的也是类似的效果,这个测试与上面的应该是同一种情况,具体原因不得而已。

有知晓原因的可以搞告诉我,谢谢。



尊重原创,转载请注明:From 凯子哥(http://blog.csdn.net/zhaokaiqiang1992) 侵权必究!

关注我的微博,可以获得更多精彩内容

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-19 23:00:45

关于Android中图片大小、内存占用与drawable文件夹关系的研究与分析的相关文章

Android 适配(drawable文件夹)图片适配(二)

参考自(https://blog.csdn.net/myoungmeng/article/details/54090891) Android资源文件存放: android的drawable文件一共可以有: drawable-ldpi(低密度)               drawable-mdpi(中等密度)              drawable-hdpi(高密度)         drawable-xhdpi(超高密度) drawable-xxhdpi(超超高密度)     drawab

Android中图片的三级缓存策略

一.简介 现在的Android应用程序中,不可避免的都会使用到图片,如果每次加载图片的时候都要从网络重新拉取,这样不但很耗费用户的流量,而且图片加载的也会很慢,用户体验很不好.所以一个应用的图片缓存策略是很重要的.通常情况下,Android应用程序中图片的缓存策略采用"内存-本地-网络"三级缓存策略,首先应用程序访问网络拉取图片,分别将加载的图片保存在本地SD卡中和内存中,当程序再一次需要加载图片的时候,先判断内存中是否有缓存,有则直接从内存中拉取,否则查看本地SD卡中是否有缓存,SD

Android 中图片压缩分析(上)

作者: shawnzhao,QQ音乐技术团队一员 一.前言 在 Android 中进行图片压缩是非常常见的开发场景,主要的压缩方法有两种:其一是质量压缩,其二是下采样压缩. 前者是在不改变图片尺寸的情况下,改变图片的存储体积,而后者则是降低图像尺寸,达到相同目的. 由于本文的篇幅问题,分为上下两篇发布. 二.Android 质量压缩逻辑 在Android中,对图片进行质量压缩,通常我们的实现方式如下所示: ByteArrayOutputStream outputStream = new Byte

Android中图片的处理(放大缩小,去色,转换格式,增加水印等)(转)

Android中图片的处理(放大缩小,去色,转换格式,增加水印等) 原文地址:http://menxu.lofter.com/post/164b9d_3ebf79 package com.teamkn.base.utils; import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.

浅谈android中图片处理之图形变换特效Matrix(四)

今天,我们就来谈下android中图片的变形的特效,在上讲博客中我们谈到android中图片中的色彩特效来实现的.改变它的颜色主要通过ColorMatrix类来实现. 现在今天所讲的图片变形的特效主要就是通过Matrix类来实现,我们通过上篇博客知道,改变色彩特效,主要是通过ColorMatrxi矩阵的系数,以及每个像素点上所对应的颜色偏移量.而今天的图形变换与那个也是非常的类似.它是一个3*3矩阵,而颜色矩阵则是一个4*5的矩阵.在这个3*3矩阵中则表述出了每个像素点的XY坐标信息.然后通过修

cocos2dx 中切换场景内存占用过高的处理

cocos2dx 中切换场景内存占用过高的处理 1.运行场景: CCScene *pScene = HelloWorld::scene(); pDirector->runWithScene(pScene); 2.替换场景: (1) CCScene *pScene=SceneTestScene::scene(); CCDirector::sharedDirector()->replaceScene(pScene); (2) CCScene *pScene=SceneTestScene::scen

Android中常见的内存泄漏

为什么会产生内存泄漏? 当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏. 内存泄漏对程序的影响? 内存泄漏是造成应用程序OOM的主要原因之一!我们知道Android系统为每个应用程序分配的内存有限,而当一个应用中产生的内存泄漏比较多时,这就难免会导致应用所需要的内存超过这个系统分配的内存限额,这就造成了内存溢出而导致应用Crash. Android中常见的内存泄漏汇总 1

Android中使用Apache common ftp进行下载文件

在Android使用ftp下载资源 可以使用ftp4j组件,还可以用apache common net里面的ftp组件,这2个组件我都用过. 个人感觉Apache common net里面的组件比较好用一些,下面是一个实例. 项目中对ftp的使用进行了封装,添加了回调函数已经断点续传的方法. FTPCfg 用来存储ftp地址,密码等信息的. FTPClientProxy 只是个代理而已,里面主要封装了common ftp api IRetrieveListener做回调用的,比如用于是否下载完成

Android Studio 复制粘贴图片到drawable文件夹没有效果 - 解决方法

我想放一些图片到drawable文件夹里面,但是简单的复制文件,粘贴文件,或者拖拽文件,都不起作用.不知道为什么,之前是可以的,突然就不行了. 解决方案 在drawable文件夹的目录上右键,选择Reveal in Finder,打开drawable文件夹,再把图片复制粘贴或拖拽进来就可以啦.