Android的 ACTION_SEND意图:在将PDF附件保存到Gmail草稿箱或发送时会丢失

移动开发 2026-07-10

我正在编写一个Android应用,动态生成一个简单的PDF,然后通过一个Intent打开邮件客户端(如Gmail),并附上收件人、主题、正文以及PDF附件。

问题: Intent成功打开应用选择器,当我选择Gmail时,撰写界面会完美打开。“To”、“Subject”、“Body”以及PDF附件都在撰写界面中可见。

然而,当我点击“Send”时,它并不会真正发送。它直接跳转到草稿箱,且PDF附件不见了。只有纯文本保留。

我尝试过的做法(都无效): 为了根据现代Android(API 30+)的最佳实践解决这个问题,我尝试了以下方法:

  1. 使用 FileProvider 来正确提供 content:// URI,而不是 file:// URI。
  2. 在intent中添加 Intent.FLAG_GRANT_READ_URI_PERMISSION
  3. 添加 intent.setClipData(ClipData.newRawUri("PDF", uri)),这是某些现代应用查看附件所必需的。
  4. 试着使用 ShareCompat.IntentBuilder 以确保Intent构造得无可挑剔。
  5. 测试将PDF保存到多个位置:getCacheDir()getExternalCacheDir()getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
  6. FileOutputStream 中添加 fos.getFD().sync(),以确保在触发Intent之前PDF已完全写入到物理磁盘。
  7. 在Android 11+的包可见性配置中,为Manifest添加 <queries> 用于 <action android:name="android.intent.action.SEND" />
  8. 为每个PDF使用 System.currentTimeMillis() 生成唯一文件名,以确保Gmail不会缓存旧的/损坏的草稿。

尽管如此,附件仍然在后台悄悄地丢失。

我的代码:

`MainActivity.java`
public class MainActivity extends AppCompatActivity {
    private static final String RECIPIENT = "[email protected]";
    private static final String SUBJECT = "Test Email";
    private static final String BODY = "Test Email";
    private static final String FILE_PROVIDER_AUTHORITY = "com.mike.email.fileprovider";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btnEmailPDF).setOnClickListener(v -> sendEmailWithPdf());
    }

    private void sendEmailWithPdf() {
        File pdfFile = createPdf();
        if (pdfFile == null || !pdfFile.exists()) return;

        Uri contentUri = FileProvider.getUriForFile(this, FILE_PROVIDER_AUTHORITY, pdfFile);

        Intent intent = ShareCompat.IntentBuilder.from(this)
                .setType("application/pdf")
                .setEmailTo(new String[]{RECIPIENT})
                .setSubject(SUBJECT)
                .setText(BODY)
                .setStream(contentUri)
                .getIntent();

        // Granting permissions
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setClipData(ClipData.newRawUri("PDF Attachment", contentUri));

        startActivity(Intent.createChooser(intent, "Send Email with PDF..."));
    }

    private File createPdf() {
        PdfDocument document = new PdfDocument();
        PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(595, 842, 1).create();
        PdfDocument.Page page = document.startPage(pageInfo);

        Canvas canvas = page.getCanvas();
        Paint paint = new Paint();
        paint.setTextSize(18);
        canvas.drawText("Hello, this is a test PDF document.", 50, 50, paint);
        document.finishPage(page);

        File baseDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
        File directory = new File(baseDir, "EmailTests");
        if (!directory.exists()) directory.mkdirs();

        // Unique filename
        File file = new File(directory, "EmailTest_" + System.currentTimeMillis() + ".pdf");

        try (FileOutputStream fos = new FileOutputStream(file)) {
            document.writeTo(fos);
            fos.flush();
            fos.getFD().sync(); // Ensure physical write
        } catch (IOException e) {
            return null;
        } finally {
            document.close();
        }
        return file;
    }
}

`AndroidManifest.xml`
<queries>
    <intent>
        <action android:name="android.intent.action.SEND" />
        <data android:mimeType="application/pdf" />
    </intent>
</queries>

<application ...>
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.mike.email.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
</application>

`res/xml/file_paths.xml`
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-files-path name="my_pdfs" path="Documents/EmailTests" />
</paths>

是否存在一个已知问题:在较新版本Android上 Gmail会丢弃FileProvider附件,还是我在intent委托中遗漏了某个边缘情况?任何帮助都将不胜感激!

解决方案

你在构建 Intent 的工作中使用了 setStream() 作为 ShareCompat.IntentBuilder 的一部分。随后你通过调用 setClipData() 修改了 Intent… 这正是 ShareCompat.IntentBuilder 的作用。我的猜测是你对 setClipData() 的调用干扰了 ShareCompat.IntentBuilder 的运作,因此移除此调用后得到的结果会更稳定。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章