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

Android应用无法显示Documents文件夹中安装前的PDF文件

Android应用无法识别Documents文件夹中安装前已存在的PDF文件

我开发的Android应用通过ListView展示手机Documents文件夹中的PDF文件,新创建的文件能正常显示,但应用安装前已存在的PDF文件无法被识别(这些文件仍在文件夹中,可通过手机文件管理器查看)。

现有代码

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    private var pdfFileNames = arrayOf<String>() // PDF文件名列表
    private lateinit var pdfAdapter: ArrayAdapter<String> // ListView适配器

    private lateinit var listView: ListView

    private val documentsDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)

    @SuppressLint("MissingInflatedId")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("MainActivity", "启动相机")

        // 配置VIEWBINDING
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 初始化界面元素
        listView = binding.listaPdf

        // 初始化ListView适配器
        pdfAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, pdfFileNames)
        listView.adapter = pdfAdapter

        listView.setOnItemClickListener { parent, view, position, id ->
            val selectedFileName = pdfFileNames[position] // 获取选中的PDF文件名
            val selectedFile = File(documentsDirectory, selectedFileName) // 获取对应的PDF文件
            openPdf(selectedFile) // 打开选中的PDF文件
        }
    }

    override fun onResume() {
        super.onResume()
        loadPDFs()
    }

    
    // 加载PDF列表
    // 加载PDF文件名的函数
    private fun loadPDFs() {
        if (documentsDirectory.exists() && documentsDirectory.isDirectory) {
            pdfFileNames = documentsDirectory
                .listFiles { file -> file.isFile && file.name.toLowerCase().endsWith(".pdf") }
                .map { it.name }
                .toTypedArray()

            if(pdfFileNames.isNotEmpty()){
                // 更新ListView适配器
                pdfAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, pdfFileNames)
                listView.adapter = pdfAdapter
            }else{
                Toast.makeText(this, "在Documents文件夹中未找到PDF文件",Toast.LENGTH_SHORT).show()
            }
        }
    }

        
    // 打开PDF
    private fun openPdf(pdfFile: File) {
        val intent = Intent(this, VisorPDF::class.java)
        intent.putExtra("pdf_path", pdfFile.absolutePath)
        startActivity(intent)
    }
}

已尝试操作

  • 在onCreate和onResume中调用loadPDFs函数,问题未解决
  • 添加日志后确认:应用仅能检测到新创建的3个PDF文件,无法识别安装前已存在的8个文件(文件夹中共11个文件)
  • 排查手机语言导致的Documents文件夹名称变更问题,确认新旧文件路径完全一致

问题原因及解决方案

核心原因

Android 10(API 29)及以上版本引入**分区存储(Scoped Storage)**机制,应用默认仅能访问自身创建的文件,无法直接读取安装前就存在的公共目录文件。新创建的文件属于应用沙箱可访问范围,因此能被正常检测到,旧文件则不在该范围内。

具体解决步骤

1. 适配分区存储权限

根据系统版本申请对应权限:

  • 在AndroidManifest.xml中添加权限声明:
    <!-- Android 11+ 需申请全文件访问权限 -->
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
        android:minSdkVersion="30" />
    <!-- Android 10及以下 需申请读取外部存储权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="29" />
    
  • 在代码中动态申请权限:
    private fun checkStoragePermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // Android 11+ 跳转到权限设置页
            if (!Environment.isExternalStorageManager()) {
                val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
                val uri = Uri.fromParts("package", packageName, null)
                intent.data = uri
                startActivity(intent)
            }
        } else {
            // Android 10及以下 请求读取权限
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 100)
            }
        }
    }
    
    在onCreate方法中调用checkStoragePermission(),确保获取权限后再加载文件。
2. 改用MediaStore API查询公共文件(推荐)

分区存储下,官方推荐使用MediaStore API访问公共目录文件,无需全文件访问权限,更符合规范。修改loadPDFs函数:

private fun loadPDFs() {
    val pdfList = mutableListOf<String>()
    // 查询投影字段:只获取文件名
    val projection = arrayOf(MediaStore.Files.FileColumns.DISPLAY_NAME)
    // 查询条件:MIME类型为PDF,路径包含Documents目录
    val selection = "${MediaStore.Files.FileColumns.MIME_TYPE} = ? AND ${MediaStore.Files.FileColumns.RELATIVE_PATH} LIKE ?"
    val selectionArgs = arrayOf("application/pdf", "%${Environment.DIRECTORY_DOCUMENTS}%")
    // 按添加时间倒序排列
    val sortOrder = "${MediaStore.Files.FileColumns.DATE_ADDED} DESC"

    contentResolver.query(
        MediaStore.Files.getContentUri("external"),
        projection,
        selection,
        selectionArgs,
        sortOrder
    )?.use { cursor ->
        val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)
        while (cursor.moveToNext()) {
            val fileName = cursor.getString(nameColumn)
            pdfList.add(fileName)
        }
    }

    // 更新适配器数据
    pdfAdapter.clear()
    pdfAdapter.addAll(pdfList)
    pdfAdapter.notifyDataSetChanged()

    if (pdfList.isEmpty()) {
        Toast.makeText(this, "在Documents文件夹中未找到PDF文件", Toast.LENGTH_SHORT).show()
    }
}
3. 优化适配器更新逻辑

避免重复创建ArrayAdapter,初始化时使用可变列表,后续直接更新数据源并通知刷新:

// onCreate中初始化适配器
pdfAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, mutableListOf())
listView.adapter = pdfAdapter

内容的提问来源于stack exchange,提问作者Hugo Jiménez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 22:20:33