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

Android读取手机内部存储JSON文件遇权限及路径获取问题

问题修复方案

一、解决「Error getting file path」错误

你的getPathFromUri函数存在核心问题:使用MediaStore.Images.Media.DATA列查询JSON文件,这是图片媒体库专属列,无法匹配JSON这类通用文件,导致返回null触发错误提示。此外,Android 10+的Scoped Storage限制了直接获取文件路径的能力,正确做法是直接通过Uri读取文件内容,无需转换为路径。

修复步骤:

  1. 删除getPathFromUri函数,修改onActivityResult直接传递Uri给ViewModel:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    if (requestCode == FILE_PICK_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        data?.data?.let { uri ->
            view?.findViewById<TextView>(R.id.selectedItemTv)?.text = "Selected item: ${uri.toString()}"
            selectedFileUri = uri // 直接保留Uri,无需转换为file://路径
            // 调用ViewModel的处理函数
            viewModel.handleFileSelection(requireActivity().application, requireContext(), uri) { msg ->
                Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show()
            }
        }
    }
}

二、解决testPush函数的FileNotFoundException(权限拒绝)

Environment.getExternalStoragePublicDirectory在Android 10(API 29)已被废弃,且Scoped Storage限制了对公共目录的直接访问,即使申请了MANAGE_EXTERNAL_STORAGE,也需要用户手动在系统设置中为应用开启「所有文件访问权限」,该权限无法通过常规权限申请流程获取。

替代方案:

放弃硬编码路径读取文件,改用文件选择器让用户手动选择ads_interests.json,或者通过MediaStore查询该文件:

// 通过MediaStore查询Downloads目录下的ads_interests.json
fun queryAdsInterestsFile(application: Application): Uri? {
    val projection = arrayOf(MediaStore.Files.FileColumns._ID)
    val selection = "${MediaStore.Files.FileColumns.DISPLAY_NAME} = ? AND ${MediaStore.Files.FileColumns.RELATIVE_PATH} = ?"
    val selectionArgs = arrayOf("ads_interests.json", "${Environment.DIRECTORY_DOWNLOADS}/")

    val cursor = application.contentResolver.query(
        MediaStore.Files.getContentUri("external"),
        projection,
        selection,
        selectionArgs,
        null
    )

    cursor?.use {
        if (it.moveToFirst()) {
            val id = it.getLong(it.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID))
            return MediaStore.Files.getContentUri("external").buildUpon().appendPath(id.toString()).build()
        }
    }
    return null
}

// 使用查询到的Uri读取文件
fun testPush(application: Application) {
    val fileUri = queryAdsInterestsFile(application) ?: return
    try {
        application.contentResolver.openInputStream(fileUri)?.use { inputStream ->
            val result = inputStream.bufferedReader().readText()
            myDataReference.setValue(result)
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

若必须使用MANAGE_EXTERNAL_STORAGE,需添加代码引导用户开启权限:

fun requestManageStoragePermission(context: Context) {
    if (!Environment.isExternalStorageManager()) {
        val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
        intent.data = Uri.parse("package:${context.packageName}")
        context.startActivity(intent)
    }
}

三、修复handleFileSelection函数的逻辑错误

该函数错误地尝试从assets文件夹读取文件,而用户选择的是外部存储文件,需改为通过ContentResolver读取Uri对应的文件内容:

fun handleFileSelection(
    application: Application,
    appContext: Context,
    fileUri: Uri,
    callback: (String) -> Unit
) {
    viewModelScope.launch(Dispatchers.IO) {
        try {
            // 通过ContentResolver读取外部文件
            val inputStream = application.contentResolver.openInputStream(fileUri)
            inputStream?.use { stream ->
                val jsonString = stream.bufferedReader().readText()

                // Parse JSON using Gson
                val gson = Gson()
                val jsonData = gson.fromJson(jsonString, jsonData::class.java)

                // Upload interests to Firebase
                uploadInterests(jsonData)
                callback("File uploaded successfully")
            } ?: callback("Failed to open file")
        } catch (e: Exception) {
            e.printStackTrace()
            callback("Error: ${e.message}")
        }
    }
}

四、其他注意事项

  • 移除openFilePicker中不必要的MediaStore.EXTRA_OUTPUT参数,ACTION_GET_CONTENT无需该参数,它用于拍照等场景输出文件。
  • 确保FileProvider配置正确(在AndroidManifest.xml中声明,且paths.xml包含对应路径),不过文件选择器返回的Uri通常是系统提供的,无需自行处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 11:07:43