上一篇
安卓存储图片资源
- 行业动态
- 2025-04-23
- 4959
安卓存储图片资源可通过文件系统(内部/外部 存储)、SQLite数据库或ContentProvider实现,内部存储使用getFilesDir(),外部需申请权限并遵循MediaStore规范,数据库适合结构化管理,ContentProvider支持跨应用共享,缓存机制可优化加载
存储位置选择
内部存储(Internal Storage)
- 路径:
/data/data/包名/files/
- 特点:
- 无需申请存储权限(默认可读写)
- 数据仅限当前应用访问
- 卸载应用时自动清除
- 适用场景:私密图片存储(如用户头像、缓存图片)
外部存储(External Storage)
- 路径:
- 公有目录:
/sdcard/DCIM/
、/sdcard/Pictures/
- 应用专属目录:
/sdcard/Android/data/包名/
- 公有目录:
- 特点:
- 需申请
WRITE_EXTERNAL_STORAGE
权限(Android 6.0+需动态申请) - 数据可被其他应用或用户访问
- Android 10+ 受Scoped Storage限制
- 需申请
- 适用场景:用户生成的媒体文件(如拍照保存)
SD卡存储
- 注意:Android 11后直接访问SD卡受限,需通过MediaStore API
- 特点:
- 可移除存储介质
- 兼容性较差(部分设备不支持)
- 替代方案:使用外部存储公有目录
存储格式对比
存储方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
文件系统 | 简单直接,支持标准IO操作 | 大文件管理困难,无索引查询 | 本地缓存、临时图片 |
SQLite数据库 | 支持事务、索引查询 | 存储BLOB效率低,不适合大量图片 | 结构化数据+少量图片 |
ContentProvider | 跨应用共享数据 | 实现复杂,需处理URI解析 | 图片分享、多应用协作 |
云存储 | 无限扩容、多端同步 | 依赖网络、存在流量消耗 | 用户数据备份、多设备同步 |
图片压缩与优化
压缩策略
- 质量压缩:通过
Bitmap.compress()
方法降低JPEG质量(0-100) - 尺寸压缩:使用
BitmapFactory.Options
设置inSampleSize
按比例缩放 - 格式转换:WebP格式相比JPEG/PNG体积减少30%-50%
代码示例
// 尺寸压缩 BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 4; // 宽高缩小为1/4 Bitmap bitmap = BitmapFactory.decodeFile("path", options); // WebP压缩 ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.WEBP, 80, baos); // 质量80%
权限处理(Android 6.0+)
权限类型 | 申请方式 | 说明 |
---|---|---|
READ_EXTERNAL_STORAGE | 危险权限,需动态申请 | 读取外部存储图片 |
WRITE_EXTERNAL_STORAGE | 危险权限,需动态申请 | 写入/删除外部存储文件 |
MANAGE_EXTERNAL_STORAGE | 系统设置页申请(Android 11+) | 管理所有文件权限(谨慎使用) |
常见问题与解决方案
Android 10+ 沙盒机制适配
- 问题:直接访问外部存储路径会报
FileUriExposedException
- 解决方案:
- 使用
MediaStore.Images.Media.insertImage()
插入系统图库 - 通过
ContentResolver
操作MediaStore数据库 - 申请
READ_MEDIA_IMAGES
等细分权限(Android 13+)
- 使用
大图片内存溢出(OOM)
- 原因:直接加载超过VM堆大小的图片
- 解决方案:
- 分区域加载:
BitmapRegionDecoder
- 采样率压缩:
options.inSampleSize
- 异步加载:配合
LruCache
缓存
- 分区域加载:
问题与解答
Q1:如何动态申请外部存储写入权限?
A:
- 在
AndroidManifest.xml
声明权限:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- 运行时检查并申请:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE); }
- 处理回调结果:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限已授予 } else { // 权限被拒绝 } }
Q2:如何将图片保存到系统相册并刷新媒体库?
A:
- 通过
MediaStore
插入图片:ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DATA, filePath); getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
- 发送广播通知媒体库更新(Android 4.4+):
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setDataAndType(Uri.fromFile(new File(filePath)), "image/"); sendBroadcast(intent);