首页 > 解决方案 > 导出 Room 数据库并附加到电子邮件 Android Kotlin

问题描述

我有以下代码用于导出房间数据库,然后将其附加到电子邮件中。目前,用户首先必须选择他们想要保存数据的位置,然后才能附加数据。

有没有一种方法可以做到这一点,而无需先询问用户将数据库保存在哪里?

这是我的代码:

fun exportDatabase() {
        val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
        intent.type = "*/*" // this line is a must when using ACTION_CREATE_DOCUMENT
        startActivityForResult(
            intent,
            DATABASE_EXPORT_CODE
        )
    }

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        when (requestCode) {
            DATABASE_EXPORT_CODE -> {
                val userChosenUri = data?.data
                val inStream = getDatabasePath("app_database").inputStream()
                val outStream = userChosenUri?.let { contentResolver.openOutputStream(it) }

                inStream.use { input ->
                    outStream.use { output ->
                        output?.let { input.copyTo(it) }
                        Toast.makeText(this, "Data exported successfully", Toast.LENGTH_LONG).show()
                        val emailIntent = Intent(Intent.ACTION_SEND)
                        //Set type to email
                        emailIntent.type = "vnd.android.cursor.dir/email"
                        var toEmail: String = "whatever@gmail.com"
                        emailIntent.putExtra(Intent.EXTRA_EMAIL, toEmail)
                        emailIntent.putExtra(Intent.EXTRA_STREAM, userChosenUri)
                        emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Data for Training Log")
                        startActivity(Intent.createChooser(emailIntent, "Send Email"))
                    }
                }

            }

            else ->
                Log.d("D001", "onActivityResult: unknown request code")
        }

    }

标签: androidkotlinuriandroid-roomandroid-fileprovider

解决方案


您需要使用FileProvider。但FileProvider不支持直接传输数据库文件(在此处查看)。

这可以通过以下方式处理:

解决方案1:

创建一个FileProvider支持复制数据库文件的自定义类:

class DBFileProvider : FileProvider {
    
    fun getDatabaseURI(c: Context, dbName: String?): Uri? {
        val file: File = c.getDatabasePath(dbName)
        return getFileUri(c, file)
    }

    private fun getFileUri(context: Context, file: File): Uri? {
        return getUriForFile(context, "com.android.example.provider", file)
    }
    
}

并请求FileProvider清单:

<application>

    ....

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

</application>

provider_paths在下创建res\xml

<?xml version="1.0" encoding="utf-8"?>

<paths>

    <files-path
        name="databases"
        path="../" />

</paths>

然后通过电子邮件发送此数据库文件:

public static void backupDatabase(AppCompatActivity activity) {
    Uri uri = new DBFileProvider().getDatabaseURI(activity, "app_database.db");
    sendEmail(activity, uri);
}
private fun sendEmail(activity: AppCompatActivity, attachment: Uri) {
    val emailIntent = Intent(Intent.ACTION_SEND)
    //Set type to email
    emailIntent.type = "vnd.android.cursor.dir/email"
    val toEmail = "whatever@gmail.com"
    emailIntent.putExtra(Intent.EXTRA_EMAIL, toEmail)
    emailIntent.putExtra(Intent.EXTRA_STREAM, attachment)
    emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Data for Training Log")
    activity.startActivity(Intent.createChooser(emailIntent, "Send Email"))
}

解决方案2:

将数据库文件复制到一个临时文件到一个受FileProviderlike支持的目录filesDir

  • 使用获取数据库文件getDatabasePath
  • 将数据库文件复制到受支持的存储目录FileProvider
  • 使用FileProvider
fun backupDatabase(activity: AppCompatActivity) {

    // Get the database file
    val dbFile = activity.getDatabasePath("app_database.db")

    try {

        // Copy database file to a temp file in (filesDir)
        val parent = File(activity.filesDir, "databases_temp")
        val file = File(parent, "myDatabase")
        dbFile.copyTo(file)

        // Get Uri of the copied database file from filesDir to be used in email intent
        val uri = getUri(activity.applicationContext, file)

        // Send an email
        sendEmail(activity, uri)

    } catch (e: IOException) {
        e.printStackTrace()
    }
}


private fun getUri(context: Context, file: File): Uri {
    var uri = Uri.fromFile(file)

    // Using FileProvider for API >= 24
    if (Build.VERSION.SDK_INT >= 24) {
        uri = FileProvider.getUriForFile(
            context,
            "com.android.example.provider", file
        )
    }
    return uri
}

使用与解决方案 1 相同的清单。并使用创建的临时目录进行provider_paths调整:res\xml

<?xml version="1.0" encoding="utf-8"?>

<paths>
    <files-path
        name="databases_temp"
        path="/" />
</paths>

注意:在这两种解决方案中,将包名称调整为您的。

在此处输入图像描述


推荐阅读