You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Compose中向共享存储读写二进制文件的实现方法

Jetpack Compose 二进制文件读写(导出/导入)实现方案

针对你需要的用户可见、支持跨设备传输的二进制文件读写需求,当前Android的最优方案是基于存储访问框架(SAF),也就是你已经用到的ACTION_CREATE_DOCUMENT/ACTION_OPEN_DOCUMENT配合ContentResolver操作,无需申请传统的存储权限,完全符合Android 10+的存储政策。

一、完善导出(写入二进制数据)逻辑

你现有的代码已经能唤起文件选择器创建空白文件,接下来只需在拿到文件Uri后,通过ContentResolver写入二进制数据:

@Composable
fun ExportBinaryScreen(context: Context = LocalContext.current) {
    var pickedUri by remember { mutableStateOf<Uri?>(null) }
    // 替换成你的实际二进制数据
    val sampleBinaryData = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05)

    val exportLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        pickedUri = result.data?.data?.also { uri ->
            try {
                context.contentResolver.openOutputStream(uri)?.use { outputStream ->
                    outputStream.write(sampleBinaryData)
                    Toast.makeText(context, "导出成功", Toast.LENGTH_SHORT).show()
                }
            } catch (e: IOException) {
                e.printStackTrace()
                Toast.makeText(context, "导出失败:${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    Column(modifier = Modifier.padding(16.dp)) {
        Button(
            onClick = {
                val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
                    .apply {
                        addCategory(Intent.CATEGORY_OPENABLE)
                        type = "application/octet-stream" // 二进制文件通用MIME类型
                        putExtra(Intent.EXTRA_TITLE, "export.dat")
                    }
                exportLauncher.launch(intent)
            }
        ) {
            Text("导出二进制文件")
        }
    }
}

二、实现导入(读取二进制数据)逻辑

用ACTION_OPEN_DOCUMENT唤起文件选择器,选择已有的二进制文件后读取数据:

@Composable
fun ImportBinaryScreen(context: Context = LocalContext.current) {
    var importedData by remember { mutableStateOf<ByteArray?>(null) }

    val importLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        result.data?.data?.also { uri ->
            try {
                context.contentResolver.openInputStream(uri)?.use { inputStream ->
                    importedData = inputStream.readBytes()
                    Toast.makeText(context, "导入成功,数据长度:${importedData?.size}字节", Toast.LENGTH_SHORT).show()
                }
            } catch (e: IOException) {
                e.printStackTrace()
                Toast.makeText(context, "导入失败:${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    Column(modifier = Modifier.padding(16.dp)) {
        Button(
            onClick = {
                val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
                    .apply {
                        addCategory(Intent.CATEGORY_OPENABLE)
                        type = "application/octet-stream"
                    }
                importLauncher.launch(intent)
            }
        ) {
            Text("导入二进制文件")
        }

        importedData?.let { data ->
            Text(
                text = "导入的数据(十六进制):${data.joinToString(" ") { "%02X".format(it) }}",
                modifier = Modifier.padding(top = 16.dp)
            )
        }
    }
}

三、关键注意事项

  • 权限处理:Android 10(API 29)及以上无需申请WRITE_EXTERNAL_STORAGE或READ_EXTERNAL_STORAGE权限,SAF操作通过系统授权的Uri完成,符合系统安全规范。
  • 持久化访问权限:如果需要在应用重启后仍能访问已选择的文件,可在拿到Uri后调用:
    context.contentResolver.takePersistableUriPermission(
        uri,
        Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
    )
    
    权限会被持久化到应用的权限列表中,无需重复选择。
  • 流的安全关闭:使用use函数自动管理输入/输出流的生命周期,避免资源泄漏。
  • 自定义MIME类型:如果你的二进制数据有专属格式,可替换application/octet-stream为自定义MIME类型(比如application/vnd.yourcompany.yourapp),不影响读写,但能帮助系统识别文件类型。

内容的提问来源于stack exchange,提问作者Fergusmac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 11:43:08