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

Android 13/API33下Fragment中Documents目录文本文件写入方案

Android 13/API33 Fragment中写入公共Documents目录文本文件的解决方案

问题背景

原代码在Android13中抛出IOException,核心问题是newDir.mkdir()误将文件名当作文件夹创建,后续FileOutputStream指向文件夹而非文件导致错误。同时Android13对公共外部存储的文档类文件访问限制收紧,传统File API已无法直接操作公共Documents目录。

核心解决方案

Android13中访问公共Documents目录的文档类文件,必须通过**存储访问框架(SAF)**让用户手动选择保存位置并确认文件名,获取文件Uri后通过ContentResolver完成写入操作。以下是Fragment中的完整实现步骤:


1. 定义请求常量(Fragment类内)

private static final int REQUEST_CREATE_DOCUMENT = 1001;

2. 启动文件创建选择器(触发用户交互)

在需要创建文件的场景(如按钮点击)调用此方法,系统会打开文件选择器并默认定位到Documents目录,让用户确认保存路径和文件名:

public void startCreateDocument(String defaultFileName) {
    Intent createIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    // 标记可打开的文件类型
    createIntent.addCategory(Intent.CATEGORY_OPENABLE);
    // 指定文件为文本类型
    createIntent.setType("text/plain");
    // 设置默认文件名
    createIntent.putExtra(Intent.EXTRA_TITLE, defaultFileName);

    // Fragment中启动选择器,推荐使用AndroidX的registerForActivityResult(见下方)
    startActivityForResult(createIntent, REQUEST_CREATE_DOCUMENT);
}

推荐:使用AndroidX的registerForActivityResult(替代过时API)

在Fragment的onViewCreated中注册结果回调,避免startActivityForResult过时问题:

private ActivityResultLauncher<Intent> createDocumentLauncher;

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    createDocumentLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        result -> {
            if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
                Uri fileUri = result.getData().getData();
                if (fileUri != null) {
                    // 写入目标文本内容
                    writeContentToFile(fileUri, "这里是要写入的文本内容");
                }
            }
        }
    );
}

// 调用时直接触发launch
public void startCreateDocument(String defaultFileName) {
    Intent createIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    createIntent.addCategory(Intent.CATEGORY_OPENABLE);
    createIntent.setType("text/plain");
    createIntent.putExtra(Intent.EXTRA_TITLE, defaultFileName);
    createDocumentLauncher.launch(createIntent);
}

3. 接收结果并写入数据

实现通过ContentResolver写入文本的核心方法,适配Fragment的Context调用:

private void writeContentToFile(Uri fileUri, String content) {
    try (OutputStream outputStream = requireContext().getContentResolver().openOutputStream(fileUri)) {
        if (outputStream != null) {
            outputStream.write(content.getBytes(StandardCharsets.UTF_8));
            // Fragment中显示提示需用requireContext()/getActivity()
            Toast.makeText(requireContext(), "文件写入成功", Toast.LENGTH_SHORT).show();
        }
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(requireContext(), "写入失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    }
}

兼容旧写法:重写onActivityResult

如果仍使用startActivityForResult,需在Fragment中重写此方法:

@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CREATE_DOCUMENT && resultCode == Activity.RESULT_OK && data != null) {
        Uri fileUri = data.getData();
        if (fileUri != null) {
            writeContentToFile(fileUri, "这里是要写入的文本内容");
        }
    }
}

关键注意事项

  • 权限无需额外申请:Android13中通过SAF访问公共Documents目录,无需申请READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE权限,用户通过选择器完成授权。
  • Fragment Context注意:所有需要Context的操作(如Toast、ContentResolver)必须使用requireContext()或getActivity(),不能直接用Fragment实例。
  • 文件类型灵活调整:若需创建其他类型文件,修改createIntent.setType()的参数即可,例如application/pdf对应PDF文件。
  • 彻底替代传统File API:不再使用Environment.getExternalStoragePublicDirectory等废弃API,完全通过SAF的Uri操作文件,避免原代码中文件夹/文件混淆的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 06:05:33