Bitmap(位图)全解析 Part 1

在 Android 开发中,Bitmap(位图)是处理图像的核心类,广泛用于显示图片、绘制图形、图像处理等场景。Bitmap 属于 android.graphics 包,表示一张二维像素矩阵,包含颜色和透明度信息。由于其重要性和复杂性,本文将作为 Bitmap 全解析 Part 1,聚焦于 Bitmap 的基础知识、创建与加载方式、配置参数、内存管理基础,以及在 Android 中的常见使用场景,结合中文讲解、代码示例和注意事项,适合初学者和进阶开发者。后续部分将深入探讨图像处理、优化和高级应用。


一、Bitmap 基础知识

1. Bitmap 定义

  • Bitmap:一个表示位图图像的类,存储像素数据(RGB 或 ARGB),每个像素由颜色值和可选的透明度(Alpha 通道)组成。
  • android.graphics.Bitmap
  • 用途
  • 显示图片(ImageView、Canvas)。
  • 图像处理(如裁剪、旋转、滤镜)。
  • 自定义 Drawable 或 View 绘制。
  • 核心属性
  • 宽度和高度:以像素为单位。
  • 像素格式:如 ARGB_8888(高质量,4 字节/像素)、RGB_565(节省内存,2 字节/像素)。
  • 内存占用宽 × 高 × 每像素字节数

2. Bitmap 的像素格式

Bitmap 支持多种像素格式,影响内存占用和图像质量:

  • ARGB_8888(默认):4 字节/像素,支持 Alpha 通道,最高质量,内存占用大。
  • RGB_565:2 字节/像素,无 Alpha 通道,适合不透明图像,内存占用较小。
  • ARGB_4444(已废弃):2 字节/像素,低质量 Alpha,Android 8.0+ 不推荐。
  • ALPHA_8:1 字节/像素,仅存储 Alpha 通道,用于遮罩。
  • 代码示例(检查格式):
  val bitmap = BitmapFactory.decodeResource(resources, R.drawable.image)
  println("Bitmap 格式: ${bitmap.config}") // 输出:ARGB_8888

3. 内存占用计算

  • 公式内存占用 = 宽度 × 高度 × 每像素字节数
  • 示例
  • 1080×1920 像素,ARGB_8888 格式:1080 × 1920 × 4 = 8,294,400 字节 ≈ 8MB
  • 1080×1920 像素,RGB_565 格式:1080 × 1920 × 2 = 4,147,200 字节 ≈ 4MB
  • 注意:大尺寸 Bitmap 容易导致内存溢出(OOM),需优化加载。

二、Bitmap 的创建与加载方式

Bitmap 可以通过多种方式创建或加载,以下是常见方法:

1. 从资源文件加载

  • 场景:加载 res/drawableres/raw 中的图片。
  • 代码示例(Kotlin):
  import android.graphics.BitmapFactory
  import android.os.Bundle
  import android.widget.ImageView
  import androidx.appcompat.app.AppCompatActivity

  class MainActivity : AppCompatActivity() {
      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_main)

          val imageView = findViewById<ImageView>(R.id.imageView)
          val bitmap = BitmapFactory.decodeResource(resources, R.drawable.image)
          imageView.setImageBitmap(bitmap)
      }
  }
  • 布局文件res/layout/activity_main.xml):
  <ImageView
      android:id="@+id/imageView"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />

2. 从文件加载

  • 场景:加载 SD 卡或内部存储的图片。
  • 代码示例(Kotlin):
  import java.io.File

  val file = File("/sdcard/image.jpg")
  val bitmap = BitmapFactory.decodeFile(file.absolutePath)
  imageView.setImageBitmap(bitmap)
  • 权限(Android 6.0+):
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

3. 从输入流加载

  • 场景:从网络、Assets 或其他流加载图片。
  • 代码示例(从 Assets):
  val inputStream = assets.open("image.jpg")
  val bitmap = BitmapFactory.decodeStream(inputStream)
  imageView.setImageBitmap(bitmap)
  inputStream.close()

4. 创建空白 Bitmap

  • 场景:动态生成图片或用于 Canvas 绘制。
  • 代码示例(Kotlin):
  import android.graphics.Bitmap
  import android.graphics.Canvas
  import android.graphics.Color

  val bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888)
  val canvas = Canvas(bitmap)
  canvas.drawColor(Color.RED) // 填充红色
  imageView.setImageBitmap(bitmap)

5. 从现有 Bitmap 创建

  • 场景:裁剪、缩放或复制 Bitmap。
  • 代码示例(Kotlin):
  val sourceBitmap = BitmapFactory.decodeResource(resources, R.drawable.image)
  val croppedBitmap = Bitmap.createBitmap(sourceBitmap, 50, 50, 100, 100) // 裁剪
  imageView.setImageBitmap(croppedBitmap)

三、Bitmap 配置参数(BitmapFactory.Options)

BitmapFactory.Options 用于控制 Bitmap 加载行为,优化内存和性能。

1. 常用属性

  • inJustDecodeBounds:仅解码边界信息(宽高),不加载像素数据。
  • inSampleSize:采样率,压缩图片尺寸(2 的幂,如 2、4、8)。
  • inPreferredConfig:指定像素格式(如 Bitmap.Config.RGB_565)。
  • inMutable:是否可修改(默认 false)。

2. 优化加载示例

加载大图时,计算采样率以减少内存占用:

fun loadScaledBitmap(filePath: String, targetWidth: Int, targetHeight: Int): Bitmap? {
    // 第一次解码,仅获取宽高
    val options = BitmapFactory.Options().apply {
        inJustDecodeBounds = true
    }
    BitmapFactory.decodeFile(filePath, options)

    // 计算采样率
    options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight)
    options.inJustDecodeBounds = false
    options.inPreferredConfig = Bitmap.Config.RGB_565 // 节省内存

    // 第二次解码,加载缩放后的 Bitmap
    return BitmapFactory.decodeFile(filePath, options)
}

fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
    val (height: Int, width: Int) = options.outHeight to options.outWidth
    var inSampleSize = 1
    if (height > reqHeight || width > reqWidth) {
        val halfHeight = height / 2
        val halfWidth = width / 2
        while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}
  • 使用
  val bitmap = loadScaledBitmap("/sdcard/image.jpg", 100, 100)
  imageView.setImageBitmap(bitmap)
  • 说明
  • inJustDecodeBounds = true 获取图片尺寸。
  • inSampleSize 按 2 的幂缩放(如 2 代表宽高减半)。
  • 使用 RGB_565 减少内存。

四、Bitmap 在 Android 中的使用场景

1. ImageView 显示

  • 描述:最常见场景,将 Bitmap 设置为 ImageView 的内容。
  • 示例
  imageView.setImageBitmap(bitmap)

2. 作为 Drawable

  • 描述:将 Bitmap 转换为 BitmapDrawable,用于 View 背景。
  • 示例
  val bitmapDrawable = BitmapDrawable(resources, bitmap)
  view.background = bitmapDrawable

3. Canvas 绘制

  • 描述:在 Canvas 上绘制 Bitmap 或自定义图形。
  • 示例
  val bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888)
  val canvas = Canvas(bitmap)
  canvas.drawColor(Color.BLUE)
  canvas.drawText("Hello", 50f, 50f, Paint().apply { color = Color.WHITE })
  imageView.setImageBitmap(bitmap)

4. 图像处理

  • 描述:裁剪、缩放、旋转等(Part 2 将详细讲解)。
  • 简单示例(缩放):
  val scaledBitmap = Bitmap.createScaledBitmap(bitmap, 100, 100, true)
  imageView.setImageBitmap(scaledBitmap)

五、内存管理基础

Bitmap 是 Android 中内存占用的主要来源,需谨慎管理以避免 OOM(OutOfMemoryError)。

1. 内存优化策略

  • 使用采样率:通过 inSampleSize 减小图片尺寸。
  • 选择低内存格式:优先使用 RGB_565 而非 ARGB_8888(无透明需求时)。
  • 及时回收:调用 bitmap.recycle() 释放内存(仅当确保不再使用)。
  if (!bitmap.isRecycled) {
      bitmap.recycle()
  }
  • 缓存管理:使用 LRU 缓存(后续 Part 2 讲解)。

2. 检查内存占用

  • 代码示例
  val memorySize = bitmap.byteCount / 1024 / 1024 // MB
  println("Bitmap 内存: $memorySize MB")

3. 避免内存泄漏

  • 释放 ImageView 的 Bitmap
  override fun onDestroy() {
      super.onDestroy()
      (imageView.drawable as? BitmapDrawable)?.bitmap?.recycle()
      imageView.setImageDrawable(null)
  }

六、常见问题及注意事项

  1. 内存溢出(OOM)
  • 原因:加载大尺寸图片或未释放 Bitmap。
  • 解决
    • 使用 inSampleSize 缩放。
    • 选择 RGB_565 格式。
    • 及时调用 recycle()(谨慎使用,API 19+ 不推荐)。
  1. 兼容性
  • 低版本 API:ARGB_4444 在 Android 8.0+ 已废弃。
  • Android 4.4+:WebView 支持 SVG,可结合 VectorDrawable 替代部分 Bitmap。
  1. 性能优化
  • 异步加载:大图加载使用线程或协程:
    kotlin GlobalScope.launch(Dispatchers.IO) { val bitmap = BitmapFactory.decodeFile("/sdcard/image.jpg") runOnUiThread { imageView.setImageBitmap(bitmap) } }
  • 硬件加速:确保启用(默认开启):
    xml <application android:hardwareAccelerated="true">
  1. 权限问题
  • 加载外部存储图片需运行时权限(Android 6.0+):
    kotlin if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 100) }
  1. 资源管理
  • 关闭输入流:
    kotlin inputStream.close()
  • 避免重复加载:
    kotlin if (bitmap == null || bitmap.isRecycled) { bitmap = BitmapFactory.decodeResource(resources, R.drawable.image) }

七、学习建议与实践

  1. 学习路径
  • 掌握 Bitmap 加载方式(资源、文件、流)。
  • 学习 BitmapFactory.Options 优化内存。
  • 实现基本显示和绘制场景。
  • 了解内存管理基础。
  1. 实践项目
  • 简单项目:加载图片到 ImageView,优化内存。
  • 进阶项目:动态创建 Bitmap,绘制简单图形。
  • 高级项目:实现图片裁剪或缩放(Part 2 讲解)。
  1. 调试工具
  • Android Studio Profiler:监控内存占用。
  • Logcat:记录 Bitmap 加载日志。
  • Layout Inspector:检查 ImageView 的 Drawable。
  1. 推荐资源
  • Android 官方文档:https://developer.android.com/reference/android/graphics/Bitmap
  • BitmapFactory:https://developer.android.com/reference/android/graphics/BitmapFactory
  • 内存优化:https://developer.android.com/topic/performance/graphics/manage-memory

八、总结

  • Bitmap 基础
  • 表示二维像素矩阵,支持 ARGB_8888、RGB_565 等格式。
  • 内存占用与尺寸和格式相关。
  • 创建与加载
  • 从资源、文件、流或动态创建。
  • 使用 BitmapFactory.Options 优化加载。
  • 使用场景
  • ImageView 显示、Drawable 背景、Canvas 绘制。
  • 内存管理
  • 使用采样率、低内存格式。
  • 及时回收,防止泄漏。
  • 注意事项
  • 避免 OOM,优化加载。
  • 处理权限和兼容性。
  • 异步加载大图。

九、后续系列预告

本篇为 Bitmap 全解析 Part 1,聚焦基础知识和加载方式。后续系列将深入探讨:

  • Part 2:图像处理(裁剪、缩放、旋转、滤镜)。
  • Part 3:高级优化(LRU 缓存、图片压缩、异步加载)。
  • Part 4:结合 WebView 和 Socket 的图像传输。

如果需要更详细的代码示例(如大图加载优化、Canvas 绘制)或特定场景的讲解,请告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注