相关文章推荐
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm trying to save a file as PNG of a canvas where the user can draw something and then call an Intent.ACTION_SEND so that the user can share it's drawing with other apps.

The code is able to save the file without any problems, but when I try to use the MediaScannerConnection.scanFile() , the Uri returned by the function is null . I'm using the absolute path of the file created, so I can't understand why this is happening.

My class, called BitmapAsyncTask inherits from AsyncTask (yes, I know it's deprecated). Here's the important code:

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

** The mBitmap variable is just the Bitmap generated from the canvas.

Here, the Log.d returns, for instance:

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

I can access the file just fine if I open the Files app and search for it.

But when I run the MediaScannerConnection on onPostExecute(), the function doesn't return an uri based on the absolute path at all. Here's the code:

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"

Once again, the Log.d("Path", path) returns the same file as the previous Log.d(), but when I try to convert the Uri to string, it crashes because it's null.

I tried adding "file://" + file.absolutePath" like I saw in some other answers but it still didn't work, the uri returned by the scanFile() was still null.

I'm using 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>
                If you can save content to file then there is no need to call the mediascanner if you want to share that file. Why would you? You can serve/send the file without. Please elaborate.
– blackapps
                Sep 13, 2020 at 10:58
                Do away with that intermediate byte array output steam. You can compress the bitmap to the file output stream directly.
– blackapps
                Sep 13, 2020 at 11:00
                tried adding "file://" + file.absolutePath" like I saw in some other answers but it still didn't work. And why not? You should tell why.
– blackapps
                Sep 13, 2020 at 11:04
                You did not tell the full path where you saved the file. You could indeed use the mediascanner to obtain an uri for your file but... it is not the usual way to obtain an uri in order to be able to share. And.. the mediascanner will not scann from all all paths...
– blackapps
                Sep 13, 2020 at 11:09

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

It is valid. However, it is not indexable by MediaStore on Android 10 and higher. MediaStore will no longer index files in app-specific external storage, such as getExternalFilesDir(), which is what you are using.

If your objective is to have the image be usable by every app on the device, then getting indexed by MediaStore is fine. On Android 10+, you can insert() into the MediaStore and use the resulting Uri for writing out your content. See this sample app for a demonstration, though in my case I am writing out a video, not a PNG.

If, instead, all you want to do is share this content, then do not use MediaScannerConnection. Instead, use FileProvider. See the documentation and this sample app (though in my case I am sharing a PDF, not a PNG).

in addition, Will see /storage/emulated/0/Android/data/.nomedia if android do not index the app-specific external storage. ".nomedia" file means ignore this directory and it's subdirectory. – Rafael Zhou Apr 12, 2021 at 13:43

... 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"

... where I added the following AUTHORITY definition:

// static variables
companion object {
    private const val STORAGE_PERMISSION_CODE = 1
    private const val GALLERY = 2
    private const val AUTHORITY = "${BuildConfig.APPLICATION_ID}.fileprovider"
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.

 
推荐文章