当前位置: 首页 > news >正文

Android学习总结之Bitmap篇

一、质量压缩(面试高频:原理与适用场景)

1. 核心原理(面试官必问)

质量压缩针对有损压缩格式(如 JPEG),通过丢弃图像中人类视觉不敏感的高频信息(如色彩过渡细节),在保持视觉尺寸不变的前提下减小文件大小。

  • 关键参数Bitmap.compress(CompressFormat.JPEG, quality, outputStream),其中quality(0-100)控制压缩程度,数值越低压缩率越高(文件越小,质量越差)。
  • 内存占用不变:压缩后的图片文件大小减小,但解码后的Bitmap内存占用(像素数 × 单个像素字节)与原图一致(因未改变像素尺寸)。
2. 源码级实现与 Android 底层逻辑
// 质量压缩核心代码(面试需结合源码说明)  
public static void qualityCompression(Bitmap bitmap, int quality, String filePath) {  try (FileOutputStream fos = new FileOutputStream(filePath)) {  // 调用Native层压缩逻辑(Android Framework层)  bitmap.compress(CompressFormat.JPEG, quality, fos);  } catch (IOException e) {  e.printStackTrace();  }  
}  

  • Native 层处理Bitmap.compress最终调用android.graphics.Bitmap.nativeCompress,通过 libjpeg 库实现压缩,根据quality参数调整 DCT(离散余弦变换)的量化表,丢弃高频系数。
  • 注意:PNG 格式不支持质量压缩(因 PNG 是无损格式),调用时quality参数会被忽略。
3. 适用场景(面试必答)
  • 优化目标:减小图片文件大小(如网络传输、本地存储),而非降低内存占用(因内存占用由像素尺寸决定)。
  • 典型场景
    • 上传用户头像前压缩(兼顾清晰度与流量)。
    • 生成缩略图时保持视觉尺寸不变(如聊天表情包压缩)。
4. 优缺点(面试陷阱:与采样率压缩对比)
  • 优点
    • 操作简单,无需计算尺寸,直接通过 API 实现。
    • 保持图片视觉尺寸(宽高不变),适合需要固定显示大小的场景。
  • 缺点
    • 有损压缩,过度压缩会导致马赛克、色块(如quality<30时明显失真)。
    • 无法减少内存占用(仅减小文件大小),对预防 OOM 无帮助。

二、采样率压缩(面试核心:内存优化关键)

1. 核心原理(面试官深挖:内存计算公式)

通过inSampleSize按比例缩小图片像素尺寸,使解码后的Bitmap宽高为原图的1/inSampleSize,内存占用降为原图的1/(inSampleSize²)

  • 计算公式
    内存占用 = 宽 × 高 × 单个像素字节数(如ARGB_8888为4字节)  
    压缩后内存 = (原图宽/inSampleSize) × (原图高/inSampleSize) × 4  
    
  • 源码级实现:通过BitmapFactory.Options.inSampleSize控制,仅支持**≥1 的整数**,Android 4.4 + 允许非 2 的幂次(如 3、5),但会向下取整为最接近的 2 的幂次(如 3→2,5→4)。
2. 源码级流程(面试需画流程图)
public static Bitmap decodeSampledBitmap(String path, int reqWidth, int reqHeight) {  // 第一步:获取原图尺寸(不加载内存)  final BitmapFactory.Options options = new BitmapFactory.Options();  options.inJustDecodeBounds = true;  BitmapFactory.decodeFile(path, options);  // 第二步:计算inSampleSize(核心逻辑)  options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);  // 第三步:按采样率解码(加载压缩后的Bitmap)  options.inJustDecodeBounds = false;  return BitmapFactory.decodeFile(path, options);  
}  private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {  int inSampleSize = 1;  final int rawWidth = options.outWidth;  final int rawHeight = options.outHeight;  // 宽高均大于目标尺寸时,循环计算最大采样率  while (rawWidth / inSampleSize > reqWidth || rawHeight / inSampleSize > reqHeight) {  inSampleSize *= 2; // 早期版本需为2的幂次,现支持动态计算  }  return inSampleSize;  
}  
  • Native 层解码BitmapFactory.decodeFile调用nativeDecodeAsset,根据inSampleSize对像素数据进行 subsampling(亚采样),跳过部分像素点。
3. 适用场景(面试高频:ListView/RecyclerView 优化)
  • 优化目标:减少Bitmap内存占用,预防 OOM(核心手段)。
  • 典型场景
    • 列表项图片(如 RecyclerView 显示大量图片,每个 Item 仅需显示 200x200,原图可能为 2000x2000)。
    • 加载高清图片时(如查看大图前先加载低分辨率预览图)。
4. 优缺点(面试对比质量压缩)
  • 优点
    • 显著降低内存占用(按采样率平方级减少),是预防 OOM 的核心手段。
    • 无损压缩(仅丢弃冗余像素,保留采样后像素的完整信息)。
  • 缺点
    • 降低图片分辨率,可能导致细节丢失(如文字边缘模糊)。
    • 需提前计算目标尺寸(reqWidth/reqHeight),依赖控件实际大小(需通过getMeasuredWidth()获取)。

三、面试高频问题与参考答案

1. “质量压缩和采样率压缩的核心区别是什么?”
    • 质量压缩:不改变像素尺寸,仅减小文件大小(适用于网络传输),内存占用不变,属有损压缩。
    • 采样率压缩:按比例缩小像素尺寸,显著减少内存占用(适用于大图加载),属无损压缩(保留采样后像素)。
2. “如何计算 inSampleSize 才能既保证清晰度又减少内存?”
    1. 通过inJustDecodeBounds获取原图宽高(outWidth/outHeight)。
    2. 目标尺寸取控件实际宽高(如 ImageView 的getMeasuredWidth())。
    3. 计算inSampleSize为原图宽高与目标宽高的最大比例(向上取 2 的幂次,Android 4.4 + 可非 2 幂次)。
    inSampleSize = Math.max(rawWidth/reqWidth, rawHeight/reqHeight);  
    inSampleSize = Integer.highestOneBit(inSampleSize); // 取最接近的2的幂次(旧版兼容)  
    
3. “为什么采样率压缩能有效预防 OOM?请写出内存占用计算公式。”
    • 内存占用与像素数成正比,采样率为n时,像素数降为1/n²,内存占用同比例减少。
    • 公式:
      原图内存 = width × height × 4(ARGB_8888)  
      压缩后内存 = (width/n) × (height/n) × 4 = 原图内存 / n²  
      
    • 例:原图 2000x2000(16MB),采样率 4 后为 500x500(1MB),内存减少 93.75%。
4. “质量压缩时,JPEG 和 PNG 有什么区别?”
    • JPEG:支持质量压缩(0-100),有损压缩,不支持透明通道,适合照片。
    • PNG:不支持质量压缩(压缩参数被忽略),无损压缩,支持透明通道,适合图标 / 图形。

四、面试加分项:进阶优化策略

1. 图片格式优化(源码级支持)
  • WebP 格式:Android 4.0 + 支持有损 WebP,4.3 + 支持无损 / 透明 WebP,同等质量下文件大小比 JPEG 小 25%-50%。
    bitmap.compress(CompressFormat.WEBP, 80, fos); // 压缩为WebP格式  
    
  • Argb8888 vs Rgb565:后者单个像素占 2 字节(内存减半),但色彩精度低,适合对色彩不敏感的场景(如旧版 Android 列表图片)。
2. 内存缓存与磁盘缓存(结合 LruCache/DiskLruCache)
  • 内存缓存:使用LruCache存储解码后的Bitmap,按 LRU 淘汰,避免重复解码。
    // 计算缓存大小为可用内存的1/8(面试必答公式)  
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
    int cacheSize = maxMemory / 8;  
    LruCache<String, Bitmap> memoryCache = new LruCache<>(cacheSize);  
    
  • 磁盘缓存:使用DiskLruCache存储压缩后的文件,减少网络请求或文件解码次数。
3. 硬件加速与复用(高级优化)
  • Bitmap 复用:通过BitmapFactory.Options.inBitmap复用现有Bitmap的内存(需尺寸、格式一致),减少内存分配 / 回收开销。
    options.inBitmap = reusableBitmap;  
    options.inSampleSize = 2;  
    options.inMutable = true; // 允许修改复用的Bitmap  
    
  • 硬件加速:开启 View 层级硬件加速(android:hardwareAccelerated="true"),将图片渲染转移到 GPU,减轻 CPU 压力。

使用场景对比

场景选择策略

1. 优先选择质量压缩的场景
  • 目标:减小文件大小(如文件存储、网络传输),且需保持显示尺寸不变

    • 典型场景
      • 上传图片到服务器(降低流量消耗),如用户头像、朋友圈图片(显示时需保持原图尺寸)。
      • 保存截图到本地(希望文件更小,但预览时需完整尺寸)。
    • 决策依据
      • 图片显示尺寸等于或接近原图尺寸(无需缩放显示),但需减小存储 / 传输体积。
      • 允许一定程度的视觉损失(如 JPEG 质量设为 60-80 时,肉眼难以察觉明显失真)。
    • 注意事项
      • 质量压缩对 PNG 无效(PNG 是无损格式,quality 参数会被忽略)。
      • 过度压缩(如质量 < 30)会导致色块、边缘模糊,需通过调试找到质量与大小的平衡点。
  • 代码示例(质量压缩):

    bitmap.compress(Bitmap.CompressFormat.JPEG, 60, outputStream); // 保持尺寸,减小文件大小
    
2. 优先选择采样率压缩的场景
  • 目标:降低内存占用(避免 OOM),且显示尺寸远小于原图尺寸

    • 典型场景
      • 列表视图(如 RecyclerView)加载大量图片,原图尺寸(如 4000×3000)远大于显示尺寸(如 200×200)。
      • 内存受限的低端设备加载高清图片,需提前缩放至显示所需尺寸。
    • 决策依据
      • 显示区域的宽高 ≤ 原图宽高的 1/2(需计算 inSampleSize,使加载后的像素尺寸接近显示尺寸)。
      • 核心目标是减少 Bitmap 占用的内存(如加载 100 张图片时,内存占用从 100MB 降至 25MB)。
    • 实现步骤(关键源码逻辑):
      1. 获取原图尺寸(不加载像素数据):
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true; // 仅解码图片边界,不加载像素
        BitmapFactory.decodeFile(imagePath, options);
        
      2. 计算最佳 inSampleSize(保证加载后的尺寸 ≥ 显示尺寸):
        int reqWidth = 200; // 显示宽度
        int reqHeight = 200; // 显示高度
        int width = options.outWidth;
        int height = options.outHeight;
        int inSampleSize = 1;
        while (width / 2 >= reqWidth && height / 2 >= reqHeight) {width /= 2;height /= 2;inSampleSize *= 2; // 最终 inSampleSize 为 2 的幂次,确保解码效率
        }
        
      3. 按采样率加载图片
        options.inJustDecodeBounds = false;
        options.inSampleSize = inSampleSize;
        Bitmap scaledBitmap = BitmapFactory.decodeFile(imagePath, options); // 内存占用降低为 1/inSampleSize²
        
  • 注意事项

    • inSampleSize 必须是 2 的幂次(如 1, 2, 4),非 2 的幂次会被向下取整为最接近的 2 的幂次(Android 源码逻辑)。
    • 采样率压缩后,若需进一步减小文件大小(如保存到本地),可结合质量压缩(先缩放尺寸,再压缩质量)。
3. 两者结合使用的场景
  • 目标:同时优化内存占用和文件大小
    • 典型场景
      • 从相册选取图片后,先按显示尺寸采样率压缩(降低内存),再按中等质量压缩(减小文件以便上传)。
      • 生成缩略图:先通过采样率压缩到目标尺寸(如 500×500),再用质量压缩(如 JPEG 质量 70)保存。
    • 执行顺序先采样率压缩(减少像素数),再质量压缩(优化存储体积)
      • 原因:采样率压缩直接减少像素数量,质量压缩在已缩小的像素基础上进一步优化,效率更高。

面试高频问题延伸

1. 为什么质量压缩不影响 Bitmap 的内存占用?
  • 答:Bitmap 的内存占用由像素尺寸(宽 × 高)和像素格式(如 ARGB_8888 每个像素占 4 字节)决定。质量压缩仅改变存储的文件大小(如 JPEG 编码时丢弃细节),但解码后的 Bitmap 宽高不变,因此内存占用不变。
2. 采样率压缩时,如何计算 inSampleSize 才能避免图片模糊?
  • 答:inSampleSize 应保证加载后的尺寸 略大于或等于显示尺寸。例如,显示区域为 200×200,原图为 2000×2000,则 inSampleSize=10(2000/10=200),加载后的尺寸刚好匹配显示区域,不会模糊。若 inSampleSize 过大(如 20),加载后尺寸为 100×100,显示时需拉伸至 200×200,反而会模糊,此时应取 inSampleSize=10
3. 能否对 PNG 图片进行采样率压缩?
  • 答:可以。采样率压缩是通过 inSampleSize 缩放像素尺寸,与图片格式无关(PNG/JPEG 均适用)。但 PNG 是无损格式,质量压缩对其无效(设置 quality 参数会被忽略),因此 PNG 只能通过采样率压缩来减少内存占用和文件大小。

总结:选择公式

  1. 若需控制内存占用(防 OOM),且显示尺寸 < 原图尺寸 → 优先采样率压缩(必须做)。
  2. 若需减小文件大小(存储 / 传输),且显示尺寸 = 原图尺寸 → 优先质量压缩(可选做)。
  3. 复杂场景(如先显示再保存)→ 先采样率压缩(降内存),再质量压缩(降文件大小)(最佳实践)。

相关文章:

  • 单片机-89C51部分:8、定时器
  • Leetcode 3534. Path Existence Queries in a Graph II
  • 每日一道leetcode(不会做学习版,多学一题)
  • 【Redis】缓存|缓存的更新策略|内存淘汰策略|缓存预热、缓存穿透、缓存雪崩和缓存击穿
  • chatshare.xyz激活码使用说明和渠道指南!
  • JavaScript 中深拷贝浅拷贝的区别?如何实现一个深拷贝?
  • mybatis传递多个不同类型的参数到mapper xml文件
  • 本地大模型编程实战(28)查询图数据库NEO4J(1)
  • 苍穹外卖心得体会
  • 笔试专题(十二)
  • 【动态导通电阻】 GaN PiN二极管电导调制对动态 RON 的影响
  • PDF编辑器:Foxit PDF Editor Pro 版功能解析
  • Ubuntu如何查看硬盘的使用情况,以及挂载情况。
  • 浏览器自动化工具:Selenium 和 Playwright
  • 什么是全景相机?
  • 机器人--相机
  • 学习海康VisionMaster之线圆测量
  • stm32wb55rg (4) 启用usart串口
  • OpenAI Embedding 和密集检索(如 BERT/DPR)进行语义相似度搜索有什么区别和联系
  • transformer-实现单层Decoder 层
  • 关税互降后的外贸企业:之前暂停的订单加紧发货,后续订单考验沟通谈判能力
  • 音乐节困于流量
  • 92岁上海交大退休教师捐赠百万元给学校,其父也曾设奖学金
  • 泽连斯基:正在等待俄方确认参加会谈的代表团组成
  • 媒体:“西北大学副校长范代娣成陕西首富”系乌龙,但她的人生如同开挂
  • 习近平同巴西总统卢拉共同出席合作文件签字仪式