当应用程序安装在 SD 卡而非内部存储上时,DownloadManager 广播 DownloadManager.ERROR_FILE_ERROR

DownloadManager broadcasts DownloadManager.ERROR_FILE_ERROR when app is installed on SD card and not internal storage

我有一个使用 DownloadManager 下载多个文件的应用程序。在 SD 卡上安装应用程序会导致广播接收器在 COLUMN_REASON 中接收到带有 DownloadManager.ERROR_FILE_ERROR 的意图。

当安装在内部存储上时,该应用程序可以正常运行。设置清单属性 android:installLocation="internalOnly" 不会强制遇到问题的用户进行内部安装。

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) 的 return 值因安装位置而异。

在内部存储上它看起来符合预期:/storage/emulated/0/Download 在外部存储上它不会:/storage/<random_looking_series_of_numbers_and_letters>/Download

目的是将文件从下载目录移动到应用程序的文件目录。稍后,这些文件将根据需要从它们在文件目录中的位置复制到更具体的位置,但仍保留在文件目录中。但是,当尝试第二次复制时,会抛出源文件不存在的异常。

fun generateDownloadRequest(url: String, destination: String): DownloadManager.Request {
    val uri = Uri.parse(url)
    val request = DownloadManager.Request(uri)
    request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
    request.setTitle(destination)
    request.setDescription("Downloading ${destination.substringAfterLast(":")}.")
    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, destination)
    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).mkdirs()
    return request
}

class DownloadBroadcastReceiver : BroadcastReceiver() {
    private var doOnReceived: (Long) -> Unit = {}

    fun setDoOnReceived(action: (Long) -> Unit) {
        doOnReceived = action
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        val downloadedId = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
        downloadedId?.let {
            if (it == -1L) return@let
            doOnReceived(it)
        }
    }
}

我用于下载的文件名使用冒号作为分隔符,但冒号在 FAT 文件系统上不是有效字符。将分隔符更改为连字符解决了问题。