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/drawable
或res/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)
}
六、常见问题及注意事项
- 内存溢出(OOM):
- 原因:加载大尺寸图片或未释放 Bitmap。
- 解决:
- 使用
inSampleSize
缩放。 - 选择
RGB_565
格式。 - 及时调用
recycle()
(谨慎使用,API 19+ 不推荐)。
- 使用
- 兼容性:
- 低版本 API:ARGB_4444 在 Android 8.0+ 已废弃。
- Android 4.4+:WebView 支持 SVG,可结合 VectorDrawable 替代部分 Bitmap。
- 性能优化:
- 异步加载:大图加载使用线程或协程:
kotlin GlobalScope.launch(Dispatchers.IO) { val bitmap = BitmapFactory.decodeFile("/sdcard/image.jpg") runOnUiThread { imageView.setImageBitmap(bitmap) } }
- 硬件加速:确保启用(默认开启):
xml <application android:hardwareAccelerated="true">
- 权限问题:
- 加载外部存储图片需运行时权限(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) }
- 资源管理:
- 关闭输入流:
kotlin inputStream.close()
- 避免重复加载:
kotlin if (bitmap == null || bitmap.isRecycled) { bitmap = BitmapFactory.decodeResource(resources, R.drawable.image) }
七、学习建议与实践
- 学习路径:
- 掌握 Bitmap 加载方式(资源、文件、流)。
- 学习
BitmapFactory.Options
优化内存。 - 实现基本显示和绘制场景。
- 了解内存管理基础。
- 实践项目:
- 简单项目:加载图片到 ImageView,优化内存。
- 进阶项目:动态创建 Bitmap,绘制简单图形。
- 高级项目:实现图片裁剪或缩放(Part 2 讲解)。
- 调试工具:
- Android Studio Profiler:监控内存占用。
- Logcat:记录 Bitmap 加载日志。
- Layout Inspector:检查 ImageView 的 Drawable。
- 推荐资源:
- 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 绘制)或特定场景的讲解,请告诉我!