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

Android 14应用生成的PDF无法在Google Files及WhatsApp中显示

问题:生成的PDF无法被Google Files及WhatsApp检索(仅Android 14真机)

问题详情

  • 应用可成功生成PDF文件,但Google Files的文档板块无法显示该文件,通过WhatsApp发送文件时也无法找到,仅能手动进入内部存储路径访问
  • 应用无任何报错日志,该问题仅在Android 14真机上出现,模拟器中正常
  • 曾尝试将getExternalStoragePublicDirectory()替换为getExternalFilesDir()并创建应用专属目录,问题仍未解决

代码片段(MainActivity.java)

package com.example.receiptgenerator;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.pdf.PdfDocument;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {
    Button createPDFButton;

    //EditText spaces
    EditText dateText;
    EditText groupText;
    EditText cycleText;
    EditText termText;
    EditText feeText;
    EditText capitalText;
    EditText interestText;
    EditText moratoriumText;
    EditText fileText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        //Definition of EditText elements
        dateText = findViewById(R.id.dateText);
        groupText = findViewById(R.id.groupText);
        cycleText = findViewById(R.id.cycleText);
        termText = findViewById(R.id.termText);
        feeText = findViewById(R.id.feeText);
        capitalText = findViewById(R.id.capitalText);
        interestText = findViewById(R.id.interestText);
        moratoriumText = findViewById(R.id.moratoriumText);
        fileText = findViewById(R.id.fileText);

        createPDFButton = findViewById(R.id.button);
        createPDFButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                createButtonAction();
            }
        });
    }

    private void createButtonAction() {
        try {
            CreatePDF();
        } catch (Exception e) {
            Log.d("mylog", e.toString());
            Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show();
        }
    }

    private void CreatePDF() {
        //This creates a PDFDocument
        PdfDocument document = new PdfDocument();
        PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(378, 567, 1).create();
        PdfDocument.Page page = document.startPage(pageInfo);

        //This handles the drawing of things
        Canvas canvas = page.getCanvas();
        Typeface consoleFont = ResourcesCompat.getFont(getBaseContext(), R.font.inconsolata_regular);

        //Paint1
        Paint paint = new Paint();
        paint.setColor(Color.BLACK);
        paint.setTypeface(consoleFont);
        paint.setTextSize(18);

        //Paint2
        Paint paint2 = new Paint();
        paint2.setColor(Color.BLACK);
        paint2.setTypeface(consoleFont);
        paint2.setTextSize(18);
        paint2.setTextAlign(Paint.Align.RIGHT);

        //This handles the content
        Bitmap background = BitmapFactory.decodeResource(getResources(), R.drawable.background);
        Bitmap newBackground = Bitmap.createScaledBitmap(background, 378, 567, true);

        //Text Strings
        String dateTextString = dateText.getText().toString();
        String groupTextString = groupText.getText().toString();
        String cycleTextString = cycleText.getText().toString();
        String termTextString = termText.getText().toString();
        String feeTextString = feeText.getText().toString();
        String capitalTextString = capitalText.getText().toString();
        String interestTextString = interestText.getText().toString();
        String moratoriumTextString = moratoriumText.getText().toString();

        //Writes PDF content
        canvas.drawBitmap(newBackground, 0, 0, new Paint(Color.WHITE));
        canvas.drawText(dateTextString, 240, 145, paint);
        canvas.drawText(groupTextString, 240, 170, paint);
        canvas.drawText(cycleTextString, 240, 195, paint);
        canvas.drawText(termTextString + " Semanas", 240, 220, paint);
        canvas.drawText(feeTextString + "/" + termText.getText().toString(), 210, 290, paint2);
        if (capitalTextString.equals("") || interestTextString.equals("") || moratoriumTextString.equals("")) {

        } else {
            canvas.drawText(String.format("%.2f", Float.parseFloat(capitalTextString)), 350, 360, paint2);
            canvas.drawText(String.format("%.2f", Float.parseFloat(interestTextString)), 350, 389, paint2);
            canvas.drawText(String.format("%.2f", Float.parseFloat(moratoriumTextString)), 350, 418, paint2);

            //totalText calc and writing
            float total = Float.parseFloat(capitalText.getText().toString())
                    + Float.parseFloat(interestText.getText().toString())
                    + Float.parseFloat(moratoriumText.getText().toString());
            canvas.drawText(String.format("%.2f", total), 350, 500, paint2);
        }

        document.finishPage(page);

        File documentsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
        String filename = fileText.getText().toString() + ".pdf";
        if (fileText.getText().toString().equals("")) {
            Toast.makeText(this, "Error: Write the file name.", Toast.LENGTH_SHORT).show();
        } else if (dateTextString.equals("") || groupTextString.equals("") || cycleTextString.equals("")
        || termTextString.equals("") || feeTextString.equals("") || capitalTextString.equals("")
        || interestTextString.equals("") || moratoriumTextString.equals("")) {
            Toast.makeText(this, "Error: Fill all fields.", Toast.LENGTH_SHORT).show();
        } else {
            File file = new File(documentsDir, filename);
            try {
                FileOutputStream fos = new FileOutputStream(file);
                document.writeTo(fos);
                document.close();
                fos.close();
                Toast.makeText(this, "Receipt created successfully.", Toast.LENGTH_SHORT).show();
            } catch (FileNotFoundException e) {
                Log.d("mylog", "Receipt failed " + e.toString());
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

解决方案

问题根源是Android 14对媒体文件索引机制的调整:生成文件后,系统不会自动扫描新增文件,导致其他应用无法检索到。需要手动触发媒体扫描器,通知系统索引该PDF文件。

步骤1:添加权限(AndroidManifest.xml)

确保已声明文件访问相关权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_DOCUMENTS" />

步骤2:生成文件后触发媒体扫描

在PDF写入完成、关闭流之后,添加以下代码通知系统扫描文件:

// 原代码中Toast之前添加
MediaScannerConnection.scanFile(
    this,
    new String[]{file.getAbsolutePath()},
    new String[]{"application/pdf"},
    null
);

步骤3:(可选)适配Android 14的文件访问策略

如果使用getExternalFilesDir(),该目录属于应用私有空间,其他应用默认无法直接访问。若要让其他应用能检索到,建议将文件保存到公共文档目录(即原代码中的Environment.DIRECTORY_DOCUMENTS),并配合媒体扫描。

关键说明

  • Android 14中,公共目录下的文件不会自动触发媒体扫描,必须手动调用MediaScannerConnection.scanFile()
  • 应用私有目录(getExternalFilesDir())的文件仅应用自身可访问,其他应用无法检索,除非通过文件分享机制主动提供

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 14:14:58