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

Android Studio中FileProvider实现txt邮件附件分享Android11失效问题求助

解决方案

一、Android 11 上Intent唤起邮箱发附件失效问题修复

你遇到的兼容问题是API 30+权限规则变更、路径映射错误、Intent写法不规范共同导致的,按以下步骤修改即可:

1. 移除无效的存储权限配置

Android 11(API 30)开始,WRITE_EXTERNAL_STORAGE、READ_EXTERNAL_STORAGE权限已废弃,你使用的getExternalFilesDir属于应用专属外部存储,不需要申请任何存储权限:

  • 删除onCreate里的存储权限申请代码
  • 删除AndroidManifest.xml里的存储权限声明

2. 修正file_paths.xml路径映射配置

你之前配置的<external-path>对应系统公共存储根目录,和你实际存储文件用的getExternalFilesDir路径不匹配,Android 11对路径校验更严格所以触发异常,修改为以下配置即可:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 对应getExternalFilesDir("AppData")的路径,name为URI别名可自定义 -->
    <external-files-path
        name="app_shared_files"
        path="AppData/" />
</paths>

3. 优化文件保存逻辑

修正原有代码里的冗余逻辑和文件名笔误:

private String SaveToExternal(String fileContent) {
    if (fileContent.isEmpty()) {
        Toast.makeText(this, "没有可保存的数据", Toast.LENGTH_SHORT).show();
        return null;
    }
    File appDir = getExternalFilesDir("AppData");
    if (appDir == null) {
        Toast.makeText(this, "存储不可用", Toast.LENGTH_SHORT).show();
        return null;
    }
    // 目录不存在则创建
    if (!appDir.exists()) {
        appDir.mkdirs();
    }
    File targetFile = new File(appDir, "DataToSend.txt");
    try (FileOutputStream fos = new FileOutputStream(targetFile)) {
        fos.write(fileContent.getBytes(StandardCharsets.UTF_8));
        Toast.makeText(this, "文件准备完成", Toast.LENGTH_SHORT).show();
        return targetFile.getAbsolutePath();
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "文件保存失败", Toast.LENGTH_SHORT).show();
        return null;
    }
}

4. 修正邮件发送Intent写法

ACTION_SENDTO搭配mailto协议的方式本身不支持附件,旧系统能运行是厂商做了兼容,Android 11上的邮箱应用都遵循官方规范所以失效,修改为以下代码:

private void sendEmail(String Body, String FilePath) {
    if (FilePath == null) return;
    File file = new File(FilePath);
    if (!file.exists()) {
        Toast.makeText(this, "附件不存在", Toast.LENGTH_SHORT).show();
        return;
    }
    try {
        Uri fileUri = FileProvider.getUriForFile(this, "com.example.XXXX.YYYY", file);
        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("message/rfc822");
        intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"XYZ@gmail.com"});
        intent.putExtra(Intent.EXTRA_SUBJECT, "邮件主题");
        intent.putExtra(Intent.EXTRA_TEXT, "尊敬的先生/女士\n\n我想要......");
        intent.putExtra(Intent.EXTRA_STREAM, fileUri);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

        Intent chooser = Intent.createChooser(intent, "选择邮件应用发送");
        // 给所有匹配的应用授予URI读取权限
        List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
        startActivity(chooser);
    } catch (Exception e) {
        e.printStackTrace();
        Toast.makeText(this, "未找到可用的邮件应用", Toast.LENGTH_SHORT).show();
    }
}

5. 按钮调用逻辑优化

增加路径非空判断避免空指针:

String rv = "需要保存为文本文件并作为附件发送的测试内容";
String dataPathFile = SaveToExternal(rv);
if (dataPathFile != null) {
    sendEmail(rv, dataPathFile);
}

二、后台直接发送带附件Gmail的实现教程

你之前找到的“允许低安全级别访问”的方案已经在2022年被谷歌全面禁用,现在只能通过Gmail API + OAuth2认证实现,步骤如下:

  1. 谷歌云控制台配置:创建新项目,开启Gmail API,创建OAuth客户端ID,填写应用包名和SHA1签名(开发版、发布版都要填),下载google-services.json放到app模块根目录
  2. 依赖配置:项目根目录build.gradle添加谷歌服务插件依赖,app模块build.gradle引入Gmail API、谷歌认证相关依赖
  3. 代码实现:先引导用户登录谷歌账号授权Gmail发送权限,构建带附件的MIME邮件转Base64URL编码,调用Gmail API的users.messages.send接口发送
    注意:该方案需要用户主动授权,无法在用户无感知的情况下后台发邮件,违反规则会被谷歌封禁账号

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 01:24:08