MediaScannerConnection.scanFile() 返回 null uri

6 人关注

我试图将一个文件保存为 PNG 一个画布,用户可以在上面画东西,然后调用 Intent.ACTION_SEND ,这样用户就可以与其他应用程序分享它的画。

代码能够顺利保存文件,但当我试图使用 MediaScannerConnection.scanFile() 时,函数返回的 Uri null 。我使用的是创建文件的绝对路径,所以我不明白为什么会发生这种情况。

我的类,叫做 BitmapAsyncTask ,继承自 AsyncTask (是的,我知道它已经废弃了)。下面是重要的代码。

Writing the file to memory:

override fun doInBackground(vararg p0: Any?): String {
    var result = ""
    try {
        val bytes = ByteArrayOutputStream()
        mBitmap.compress(Bitmap.CompressFormat.PNG, 95, bytes)
        val file = File(externalCacheDir!!.absoluteFile.toString()
                + File.separator + "KidsDrawingApp_"
                + System.currentTimeMillis() / 1000 + ".png")
        val fileOutput = FileOutputStream(file)
        fileOutput.write(bytes.toByteArray())
        fileOutput.close()
        result = file.absolutePath
    } catch (e: Exception) {
        e.printStackTrace()
    Log.d("File", result)
    return result

** mBitmap变量只是从画布上生成的Bitmap。

在此,以Log.d为例,返回。

D/File: /storage/emulated/0/Android/data/com.example.kidsdrawingapp/cache/KidsDrawingApp_1599992654.png

如果我打开Files应用程序并搜索该文件,我就可以正常访问该文件。

但当我在MediaScannerConnection上运行onPostExecute()时,该函数根本没有返回一个基于绝对路径的URI。下面是代码。

MediaScannerConnection.scanFile(this@MainActivity, arrayOf(result), null) {
    path, uri -> val shareIntent = Intent()
    Log.d("Path", path)
    Log.d("Uri", uri.toString())
    shareIntent.action = Intent.ACTION_SEND
    shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
    shareIntent.type = "image/png"
    startActivity(
        Intent.createChooser(
            shareIntent, "Share image"

再一次,Log.d("Path", path)返回的文件与之前的Log.d()相同,但是当我试图将Uri转换成字符串时,它崩溃了,因为它是空的。

我试着像在其他一些答案中看到的那样加入"file://" + file.absolutePath",但还是不行,由scanFile()返回的uri仍然是空的。

我正在使用API 21。

File Provider Code

AndroidManifest.xml

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.example.kidsdrawingapp.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true" >
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/path" />
</provider>

@xml/path.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="captured" path="Android/data/com.example.kidsdrawingapp/files" />
</paths>
    
5 个评论
如果你能将内容保存到文件中,那么如果你想分享该文件,就没有必要调用mediascanner。你为什么要这样做?你可以不通过文件来提供/发送文件。请详细说明。
摒弃那个中间的字节数组输出蒸汽。你可以直接将位图压缩到文件输出流中。
tried adding "file://" + file.absolutePath" like I saw in some other answers but it still didn't work. 那为什么不呢?你应该说出原因。
如果你直接将位图压缩到fileoutputstream,那么该文件就会被保存。
你没有告诉你保存文件的完整路径。你确实可以使用mediascanner为你的文件获得一个Uri,但是......这不是获得Uri的通常方法,以便能够分享。而且......mediascanner不会从所有的路径扫描......。
android
kotlin
android-mediascanner
Daniel Bertoldi
Daniel Bertoldi
发布于 2020-09-13
2 个回答
CommonsWare
CommonsWare
发布于 2021-08-17
已采纳
0 人赞同

我似乎不明白,如果文件被保存在手机中,并且路径是有效的,为什么不能返回有效的URI?

它是有效的。但是,在安卓10及以上版本中,它不能被 MediaStore 索引。 MediaStore 将不再索引特定应用的外部存储中的文件,如 getExternalFilesDir() ,这正是你所使用的。

如果你的目标是让图片可以被设备上的每一个应用程序使用,那么通过 MediaStore 获得索引就可以了。在安卓10以上系统中,你可以将 insert() 放入 MediaStore 中,并使用产生的 Uri 来写出你的内容。请看 这个样本应用程序 进行演示,尽管在我的例子中,我写出的是一个视频,而不是PNG。

如果,你想做的只是分享这些内容,那么请不要使用 MediaScannerConnection 。相反,使用 FileProvider 。请看 文件 这个样本应用程序 (虽然在我的例子中,我分享的是一个PDF,而不是PNG)。

此外,如果安卓没有索引特定应用的外部存储,将会看到/storage/emulated/0/Android/data/.nomedia。".nomedia "文件意味着忽略这个目录和它的子目录。
Frank
Frank
发布于 2021-08-17
0 人赞同

... in case the above solution was not fully clear to everyone - here's how I applied the suggested fix to the reported file sharing issue within the tutorial exercise "Kids Drawing App" (from "The Complete Android 10 & Kotlin Development Masterclass" at Udemy):

// offer to share content
MediaScannerConnection.scanFile(
    this@MainActivity,
    arrayOf(result),
    ) { path, _ ->
    // Use the FileProvider to get a content URI
    val requestFile = File(path)
    val fileUri: Uri? = try {
        FileProvider.getUriForFile(
            this@MainActivity,
            AUTHORITY,
            requestFile)
    } catch (e: IllegalArgumentException) {
        Log.e("File Selector",
            "The selected file can't be shared: $requestFile")
    val shareIntent = Intent()
        shareIntent.action = Intent.ACTION_SEND
        shareIntent.type = "image/png"
        shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri)
        startActivity(
            Intent.createChooser(
                shareIntent, "Share"

......在那里我添加了以下AUTHORITY定义。

// static variables
companion object {