【Android 内存优化】Bitmap 内存占用计算 ( Bitmap 图片内存占用分析 | Bitmap 内存占用计算 | Bitmap 不同像素密度间的转换 )

简介: 【Android 内存优化】Bitmap 内存占用计算 ( Bitmap 图片内存占用分析 | Bitmap 内存占用计算 | Bitmap 不同像素密度间的转换 )

文章目录

一、Bitmap 内存占用

二、Bitmap 内存占用计算示例

三、Bitmap 内存占用与像素密度

四、Bitmap 内存占用与像素密度示例





一、Bitmap 内存占用


在 Android 中 Bitmap 对象在内存中存储的的像素格式有两种 : ARGB_8888 和 RGB_555 ;



① ARGB_8888 像素格式 : Alpha ( 透明度 ) , Red ( 红 ) , Green ( 绿 ) , Blue ( 蓝 ) , 各占 1 11 字节 , 每个像素点占 4 字节 , 一张宽度 W WW, 高度 H HH 的图片 , 在内存中的大小是 W × H × 4 W \times H \times 4W×H×4 字节 ;


② RGB_555 像素格式 : Red ( 红 ) 占 5 55 位 , Green ( 绿 ) 占 6 66 位 , Blue ( 蓝 ) 占 5 55 位 , 每个像素点占 5 + 6 + 5 = 16 5 + 6 + 5 = 165+6+5=16 位 , 2 22 字节 , 一张宽度 W WW, 高度 H HH 的图片 , 在内存中的大小是 W × H × 2 W \times H \times 2W×H×2 字节 ;



Android 中 Bitmap 在内存中的大小与图片大小无关 , 只与像素格式和像素点个数有关 ;


内存中的大小只与分辨率有关 , 与磁盘大小无关 ;






二、Bitmap 内存占用计算示例


1. 获取 Bitmap 最小字节数 : 调用 Bitmap 对象的 getByteCount 方法 , 可以获取到 Bitmap 对象对应图像在内存中占用的最小字节数 ;


// 从资源文件中加载内存
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blog);
// 打印 Bitmap 对象的宽高, 字节大小
Log.i("Bitmap", bitmap.getWidth() + " , " +
                bitmap.getHeight() + " , " +
                bitmap.getByteCount());



2. 打印结果 : 宽度 5224 像素 , 高度 2678 像素 , 内存中大小为 55959488 字节 ;


2020-06-29 20:32:12.794 9675-9675/kim.hsl.bm I/Bitmap: 5224 , 2678 , 55959488



3. Bitmap 占内存大小计算 : Android 中默认使用 ARGB_8888 像素格式 , 每个像素点占 4 44 字节 , 上图宽 5224 , 高 2678;


5224 × 2678 × 4 = 55 , 959 , 488 5224 \times 2678 \times 4 = 55,959,488

5224×2678×4=55,959,488


最终 Bitmap 在内存中的大小是 55,959,488 字节 ;






三、Bitmap 内存占用与像素密度


1 . BitmapFactory.Options 中封装了两个像素密度相关的值 :



① inDensity 像素密度值 : 表示该 Bitmap 图像的像素密度值 ;


/**
 * Bitmap 图像的像素密度 ; 
 * Bitmap.setDensity(int) 操作会导致被返回的图像会被强制设置一个像素密度值 ; 
 * 假如该设置的像素密度值 inDensity 与 目标像素密度值 inTargetDensity 不同 ,  
 * 并且 inScaled 被设置成 true , 那么该 Bitmap 就会被缩放到 inTargetDensity 对应的像素密度 ,
 * 然后再返回 ; 
 * 
 * 如果该值是 0 , 那么就默认该像素密度值就是资源文件对应的像素密度值 ;
 */
public int inDensity;



② inTargetDensity 目标像素密度值 : 表示要缩放到的目标图像像素密度值 ;


/**
 * 将要被绘制的目标像素密度值 ;
 * 该值需要结合 inScaled 值使用 , 如果同时设置了 inScaled = true , 和 inDensity 像素密度值 , 
 * 在图像返回时 , 会自动将图像按照 inDensity 向 inTargetDensity 缩放 ; 
 */
public int inTargetDensity;



如果 inDensity 小 , inTargetDensity 大 , 图像会被放大到原图像的 inTargetDensity / inDensity 倍 ;


如果 inDensity 大 , inTargetDensity 小 , 图像会被缩小到原图像的 inTargetDensity / inDensity 倍 ;




2 . 设计图片在资源文件中放置规则 :



① 设计稿分辨率 480 x 320 : 图片放在 mdpi 像素密度下 ; density 1, densityDpi 160 ;


② 设计稿分辨率 800 x 480 : 图片放在 hdpi 像素密度下 ; density 1.5, densityDpi 240;


③ 设计稿分辨率 1280 x 720 : 图片放在 xhdpi 像素密度下 ; density 2, densityDpi 320;


④ 设计稿分辨率 1920 x 1080 : 图片放在 xxhdpi 像素密度下 ; density 3, densityDpi 480;



屏幕密度 density , 屏幕像素密度 densityDpi , 关系是 density x 160 = densityDpi ;



3 . 获取当前的手机像素密度值 : 调用如下代码 , 获取当前手机屏幕的像素密度值 ;


getResources().getDisplayMetrics().densityDpi


获取的测试机的像素密度是 420 ;






四、Bitmap 内存占用与像素密度示例


1 . 不同屏幕密度资源适配 : 原图 1990 x 1020 ;


将同样大小的图片 , 分别拷贝到不同的目录 , 并命名 , 打印结果 :


代码示例 :


package kim.hsl.bm;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("native-lib");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
        Log.i("Bitmap", "getResources().getDisplayMetrics().densityDpi : " +
                getResources().getDisplayMetrics().densityDpi +
                " , getResources().getDisplayMetrics().density : " +
                getResources().getDisplayMetrics().density);
        // 从资源文件中加载内存
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blog);
        // 打印 Bitmap 对象的宽高, 字节大小
        Log.i("Bitmap", "blog : " + bitmap.getWidth() + " , " +
                        bitmap.getHeight() + " , " +
                        bitmap.getByteCount());
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.blog_h);
        // 打印 Bitmap 对象的宽高, 字节大小
        Log.i("Bitmap", "blog_h : " + bitmap.getWidth() + " , " +
                bitmap.getHeight() + " , " +
                bitmap.getByteCount());
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.blog_m);
        // 打印 Bitmap 对象的宽高, 字节大小
        Log.i("Bitmap", "blog_m : " + bitmap.getWidth() + " , " +
                bitmap.getHeight() + " , " +
                bitmap.getByteCount());
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.blog_x);
        // 打印 Bitmap 对象的宽高, 字节大小
        Log.i("Bitmap", "blog_x : " + bitmap.getWidth() + " , " +
                bitmap.getHeight() + " , " +
                bitmap.getByteCount());
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.blog_xx);
        // 打印 Bitmap 对象的宽高, 字节大小
        Log.i("Bitmap", "blog_xx : " + bitmap.getWidth() + " , " +
                bitmap.getHeight() + " , " +
                bitmap.getByteCount());
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.blog_xxx);
        // 打印 Bitmap 对象的宽高, 字节大小
        Log.i("Bitmap", "blog_xxx : " + bitmap.getWidth() + " , " +
                bitmap.getHeight() + " , " +
                bitmap.getByteCount());
    }
    public native String stringFromJNI();
}




2 . 执行结果 :


2020-06-29 21:32:59.398 12296-12296/kim.hsl.bm I/Bitmap: getResources().getDisplayMetrics().densityDpi : 420 , getResources().getDisplayMetrics().density : 2.625
2020-06-29 21:32:59.551 12296-12296/kim.hsl.bm I/Bitmap: blog : 5224 , 2678 , 55959488
2020-06-29 21:32:59.628 12296-12296/kim.hsl.bm I/Bitmap: blog_h : 3483 , 1785 , 24868620
2020-06-29 21:32:59.775 12296-12296/kim.hsl.bm I/Bitmap: blog_m : 5224 , 2678 , 55959488
2020-06-29 21:32:59.828 12296-12296/kim.hsl.bm I/Bitmap: blog_x : 2612 , 1339 , 13989872
2020-06-29 21:32:59.864 12296-12296/kim.hsl.bm I/Bitmap: blog_xx : 1741 , 893 , 6218852
2020-06-29 21:32:59.894 12296-12296/kim.hsl.bm I/Bitmap: blog_xxx : 1306 , 669 , 3494856



3 . 结果分析 :



本测试机 : 屏幕密度 density = 2.625 , 屏幕像素密度 densityDpi = 420


原图 1990 x 1020 ;



① 图片放在 hdpi : 该像素密度对应 density = 1.5 , densityDpi = 240 ;


加 载 到 内 存 的 宽 度 = 1990 × 2.625 1.5 = 3 , 482.5 ‬ 加载到内存的宽度 = 1990 \times \dfrac{2.625}{1.5} = 3,482.5‬加载到内存的宽度=1990×

1.5

2.625


=3,482.5‬


加 载 到 内 存 的 高 度 = 1020 × 2.625 1.5 = 1785 加载到内存的高度 = 1020\times \dfrac{2.625}{1.5} = 1785加载到内存的高度=1020×

1.5

2.625


=1785



② 图片放在 mdpi : 该像素密度对应 density = 1 , densityDpi = 160;


加 载 到 内 存 的 宽 度 = 1990 × 2.625 1 = 5 , 223.75 加载到内存的宽度 = 1990 \times \dfrac{2.625}{1} = 5,223.75加载到内存的宽度=1990×

1

2.625


=5,223.75


加 载 到 内 存 的 高 度 = 1020 × 2.625 1 = 2 , 677.5 加载到内存的高度 = 1020\times \dfrac{2.625}{1} = 2,677.5加载到内存的高度=1020×

1

2.625


=2,677.5



③ 图片放在 xhdpi : 该像素密度对应 density = 2 , densityDpi = 320;


加 载 到 内 存 的 宽 度 = 1990 × 2.625 2 = 2 , 611.875 ‬ ‬ 加载到内存的宽度 = 1990 \times \dfrac{2.625}{2} = 2,611.875‬‬加载到内存的宽度=1990×

2

2.625


=2,611.875‬‬


加 载 到 内 存 的 高 度 = 1020 × 2.625 2 = 1 , 338.75 加载到内存的高度 = 1020\times \dfrac{2.625}{2} = 1,338.75加载到内存的高度=1020×

2

2.625


=1,338.75



④ 图片放在 xxhdpi : 该像素密度对应 density = 3 , densityDpi = 480;


加 载 到 内 存 的 宽 度 = 1990 × 2.625 3 = 1 , 741.25 ‬ 加载到内存的宽度 = 1990 \times \dfrac{2.625}{3} = 1,741.25‬加载到内存的宽度=1990×

3

2.625


=1,741.25‬


加 载 到 内 存 的 高 度 = 1020 × 2.625 3 = 892.5 ‬ 加载到内存的高度 = 1020\times \dfrac{2.625}{3} = 892.5‬加载到内存的高度=1020×

3

2.625


=892.5‬



这样原像素密度图片转换成目标像素密度图片后 , 就会得到日志中打印出来的值 ;


目录
相关文章
|
10天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
10天前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
19天前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
43 15
Android 系统缓存扫描与清理方法分析
|
5天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
6天前
|
Android开发 开发者
Android性能优化——内存管理的艺术
Android性能优化——内存管理的艺术
|
15天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
103 9
|
19天前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
29天前
|
编解码 Android开发 UED
构建高效Android应用:从内存优化到用户体验
【10月更文挑战第11天】本文探讨了如何通过内存优化和用户体验改进来构建高效的Android应用。介绍了使用弱引用来减少内存占用、懒加载资源以降低启动时内存消耗、利用Kotlin协程进行异步处理以保持UI流畅,以及采用响应式设计适配不同屏幕尺寸等具体技术手段。
47 2
|
1月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境的差异性分析
【10月更文挑战第8天】 本文旨在探讨Android和iOS两大移动操作系统在开发环境上的不同,包括开发语言、工具、平台特性等方面。通过对这些差异性的分析,帮助开发者更好地理解两大平台,以便在项目开发中做出更合适的技术选择。