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

