安卓Kotlin应用如何实现公共Documents文件夹的文件读写?
安卓Kotlin写入公共Documents目录实现方案
你当前使用的Context.openFileOutput和Context.openFileInput读写的是应用私有内部存储,路径为/data/data/[你的应用包名]/files/,该目录下的文件仅你的应用可访问,不会在系统公共目录中展示,所以无法在Documents文件夹中找到对应文件。
要实现将文件保存到系统公共Documents目录,可按以下步骤修改:
1. 清单文件权限配置
在AndroidManifest.xml中添加对应权限,做版本兼容:
<!-- 安卓9(API 28)及以下需要读写存储权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <!-- 如果targetSdkVersion=29,可在application节点添加以下属性做兼容适配 --> <application ... android:requestLegacyExternalStorage="true"> ... </application>
注意:安卓6.0(API 23)到安卓9版本,需要在代码中动态申请读写存储权限,仅静态声明不生效。
2. 写入公共Documents目录方法
替换你原有的save方法,适配不同安卓版本:
fun saveTextToPublicDocuments(nomeFile: String, testo: String, contesto: Context) { // 安卓10(API 29)及以上使用MediaStore写入公共目录,无需申请存储权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // 先查询是否已存在同名文件,存在就追加内容,不存在就新建 var targetUri: Uri? = null val projection = arrayOf(MediaStore.MediaColumns._ID) val selection = "${MediaStore.MediaColumns.DISPLAY_NAME} = ? AND ${MediaStore.MediaColumns.RELATIVE_PATH} = ?" val selectionArgs = arrayOf(nomeFile, "${Environment.DIRECTORY_DOCUMENTS}/") contesto.contentResolver.query( MediaStore.Files.getContentUri("external"), projection, selection, selectionArgs, null )?.use { cursor -> if (cursor.moveToFirst()) { val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)) targetUri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id) } } if (targetUri == null) { // 无同名文件,新建文件 val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, nomeFile) put(MediaStore.MediaColumns.MIME_TYPE, "text/plain") put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS) } targetUri = contesto.contentResolver.insert(MediaStore.Files.getContentUri("external"), contentValues) } targetUri?.let { uri -> // 加"wa"模式实现追加写入,对应你原有的MODE_APPEND逻辑 contesto.contentResolver.openOutputStream(uri, "wa")?.use { outputStream -> outputStream.write(testo.toByteArray()) } } } else { // 安卓9及以下直接写入公共Documents路径 val docsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) val targetFile = File(docsDir, nomeFile) // 第二个参数true代表追加模式 FileOutputStream(targetFile, true).use { outputStream -> outputStream.write(testo.toByteArray()) } } }
3. 读取公共Documents目录文件方法
替换原有read方法:
fun readTextFromPublicDocuments(nomeFile: String, contesto: Context): String? { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val projection = arrayOf(MediaStore.MediaColumns._ID) val selection = "${MediaStore.MediaColumns.DISPLAY_NAME} = ? AND ${MediaStore.MediaColumns.RELATIVE_PATH} = ?" val selectionArgs = arrayOf(nomeFile, "${Environment.DIRECTORY_DOCUMENTS}/") contesto.contentResolver.query( MediaStore.Files.getContentUri("external"), projection, selection, selectionArgs, null )?.use { cursor -> if (cursor.moveToFirst()) { val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)) val uri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id) contesto.contentResolver.openInputStream(uri)?.use { inputStream -> return inputStream.bufferedReader().use { it.readText() } } } } } else { val docsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) val targetFile = File(docsDir, nomeFile) if (targetFile.exists()) { return FileInputStream(targetFile).bufferedReader().use { it.readText() } } } return null }
注意事项
- 上述方法保存的文件会直接出现在系统公共Documents目录下,所有应用都可以访问
- 如果需要更灵活的文件选择交互,可使用系统存储访问框架(SAF)调用系统文件选择器,让用户主动指定保存/读取路径
- 若你需要覆盖写入而非追加,把写入时的追加模式参数改为对应覆盖参数即可
内容的提问来源于stack exchange,提问作者Livio
相关产品推荐
相关产品推荐

