在Android应用开发中,文件操作是常见的需求之一。除了内部存储外,外部存储(如SD卡)也经常用于保存用户数据、图片、日志文件等较大体积的内容。本文将详细介绍如何在Android中进行外部存储(即存储卡)的文件读写操作,包括权限申请、路径获取、文件创建与读写等内容,并提供示例代码帮助你快速上手。
一、Android中的存储类型简介
(一)内部存储(Internal Storage)
- 应用私有目录,卸载后自动删除。
- 不需要额外权限即可访问。
- 路径:
/data/data/<package-name>/
(二)外部存储(External Storage)
- 包括设备自带的“扩展存储”和插入的SD卡。
- 可被其他应用或用户访问。
- 需要申请权限才能进行读写操作。
- 路径:
/storage/emulated/0/
或/storage/sdcard1/
等。
二、权限声明与适配
从Android 6.0(API 23)开始,系统引入了运行时权限机制;从Android 10(API 29)起,Google进一步限制了对公共目录的直接访问,推荐使用Scoped Storage(作用域存储)方式。
(一)在AndroidManifest.xml
中添加权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="28"/> <!-- 仅适用于Android 9及以下 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"/>
⚠️ 注意:从Android 10开始,官方建议使用MediaStore API或Storage Access Framework来访问共享目录,避免使用直接路径访问。
三、获取外部存储路径
(一)获取外部存储根目录
File externalStorageDir = Environment.getExternalStorageDirectory();
Log.d("Path", "External Storage Dir: " + externalStorageDir.getAbsolutePath());
(二)获取特定类型的公共目录(如Download、Pictures等)
File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
Log.d("Path", "Download Dir: " + downloadDir.getAbsolutePath());
(三)获取应用专属的外部缓存目录(无需权限)
File cacheDir = getExternalCacheDir();
Log.d("Path", "App External Cache Dir: " + cacheDir.getAbsolutePath());
四、文件操作示例
(一)创建文件并写入内容
try {
File file = new File(downloadDir, "test.txt");
if (!file.exists()) {
file.createNewFile(); // 创建文件
}
FileOutputStream fos = new FileOutputStream(file);
String content = "Hello, this is a test file.";
fos.write(content.getBytes());
fos.close();
Log.d("File", "文件写入成功!");
} catch (IOException e) {
e.printStackTrace();
}
(二)读取文件内容
try {
File file = new File(downloadDir, "test.txt");
FileInputStream fis = new FileInputStream(file);
byte[] data = new byte[(int) file.length()];
fis.read(data);
fis.close();
String text = new String(data, StandardCharsets.UTF_8);
Log.d("File", "读取内容:" + text);
} catch (IOException e) {
e.printStackTrace();
}
五、运行时权限请求(针对Android 6.0及以上)
如果你的应用目标SDK版本为23及以上,必须在运行时请求权限:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
在onRequestPermissionsResult
中处理结果:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "权限已授予!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "权限被拒绝,无法执行文件操作。", Toast.LENGTH_SHORT).show();
}
}
}
六、Scoped Storage适配(Android 10+)
从Android 10开始,Google鼓励开发者使用Scoped Storage,不再允许通过绝对路径访问公共目录。以下是使用MediaStore API保存图片的简单示例:
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "my_image.jpg");
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
ContentResolver resolver = getContentResolver();
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try (OutputStream os = resolver.openOutputStream(uri)) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
} catch (IOException e) {
e.printStackTrace();
}
七、最佳实践与注意事项
建议 | 说明 |
---|---|
✅ 使用Context.getExternalFilesDir() 或getExternalCacheDir() | 这些目录不需要权限,适合存储应用专属文件。 |
✅ 使用Scoped Storage(MediaStore / SAF) | 针对Android 10及以上设备,提升兼容性。 |
❌ 避免硬编码路径 | 如/sdcard/ ,不同设备路径可能不同。 |
❌ 避免频繁读写大文件 | 易造成主线程阻塞,应使用子线程或异步任务。 |
✅ 及时关闭流资源 | 防止内存泄漏和IO异常。 |
八、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!