MediaScannerConnection.scanFile() 返回空 uri

MediaScannerConnection.scanFile() returning null uri

我正在尝试将文件另存为 canvas 的 PNG,用户可以在其中绘制内容,然后调用 Intent.ACTION_SEND,以便用户可以与其他应用分享它的绘图。

代码能够毫无问题地保存文件,但是当我尝试使用 MediaScannerConnection.scanFile() 时,函数编辑的 Uri return 是 null.我使用的是创建文件的绝对路径,所以我不明白为什么会这样。

我的 class,称为 BitmapAsyncTask 继承自 AsyncTask(是的,我知道它已被弃用)。这是重要的代码:

正在将文件写入内存:

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 变量只是从 canvas.

生成的位图

在这里,Log.d returns,例如:

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

如果我打开 Files 应用程序并搜索它,我可以很好地访问该文件。

但是当我 运行 MediaScannerConnection on onPostExecute() 时,该函数根本不会 return 基于绝对路径的 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) return 与之前的 Log.d() 是同一个文件,但是当我尝试将 Uri 转换为字符串时,它崩溃了,因为它是空的。

我尝试添加 "file://" + file.absolutePath" 就像我在其他一些答案中看到的那样,但它仍然没有用,由 scanFile() 编辑的 uri return 仍然为空.

我正在使用 API 21.

文件提供商代码

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>

I can't seem to figure out why it can't return a valid uri if the file is being saved in the phone and the path is a valid one

有效。但是,在 Android 10 及更高版本上,MediaStore 无法对其进行索引。 MediaStore 将不再索引 app-specific 外部存储中的文件,例如 getExternalFilesDir(),这是您正在使用的。

如果您的 objective 是让图像可供设备上的每个应用程序使用,那么被 MediaStore 索引就可以了。在 Android 10+ 上,您可以 insert() 进入 MediaStore 并使用生成的 Uri 来写出您的内容。请参阅 this sample app 进行演示,但就我而言,我正在编写视频,而不是 PNG。

如果您只想分享此内容,请不要使用 MediaScannerConnection。相反,使用 FileProvider。请参阅 the documentation and this sample app(尽管就我而言,我共享的是 PDF,而不是 PNG)。

...如果上面的解决方案不是每个人都清楚 - 这是我如何将建议的修复程序应用到教程练习“Kids Drawing App”(来自“The Complete Android Udemy 的 10 和 Kotlin 开发大师班):

// offer to share content
MediaScannerConnection.scanFile(
    this@MainActivity,
    arrayOf(result),
    null
    ) { 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")
        null
    }


    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 {
    private const val STORAGE_PERMISSION_CODE = 1
    private const val GALLERY = 2
    private const val AUTHORITY = "${BuildConfig.APPLICATION_ID}.fileprovider"
}