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
相关产品推荐
相关产品推荐

