从 Android 应用程序共享 SQLite 数据库,无需中间副本

Pat*_*ick 3 database sqlite android uri export

我希望允许我的 Android 应用程序的用户为他们创建的内容导出 SQLite 数据库文件。我当前的解决方案将文件复制到私有存储 ( /data/data/com.package.name/files/Content.db),然后为此文件创建一个 URI 并打开共享对话框。这是有效的,例如,允许我使用 Dropbox 导出数据库文件。这是我正在使用的代码,部分改编自/sf/answers/186331771/ -

private void exportContent() {
    copyContentToPrivateStorage();

    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("application/octet-stream");

    Uri uri = new FileProvider().getDatabaseURI(this);

    intent.putExtra(Intent.EXTRA_STREAM, uri);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    startActivity(Intent.createChooser(intent, "Backup via:"));
}

private void copyContentToPrivateStorage() {
    // From /sf/answers/186331771/
    try {
        File data = Environment.getDataDirectory();
        File sd = getFilesDir();

        if (sd.canWrite()) {
            String currentDBPath = "//data//com.package.name//databases//Content.db";
            String backupDBPath = "Content.db";
            File currentDB = new File(data, currentDBPath);
            File backupDB = new File(sd, backupDBPath);

            if (currentDB.exists()) {
                FileChannel src = new FileInputStream(currentDB).getChannel();
                FileChannel dst = new FileOutputStream(backupDB).getChannel();
                dst.transferFrom(src, 0, src.size());
                src.close();
                dst.close();
            }
        }
    } catch (Exception e) {
        Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show();
    }
}

public class FileProvider extends android.support.v4.content.FileProvider {

    public Uri getDatabaseURI(Context c) {
        File exportFile = new File(c.getFilesDir(), "Content.db");
        Uri uri = getUriForFile(c, "com.package.name.fileprovider", exportFile);

        c.grantUriPermission("*", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

        return uri;
    }
}
Run Code Online (Sandbox Code Playgroud)

看起来我应该能够从现有的数据库路径直接创建一个 URI,而不是做一个中间副本。有没有办法做到这一点?

我可以继续做中间副本,但我认为将数据库的第二个副本保留在数据目录中的时间超过必要的时间是不好的做法。在选定的应用程序使用 URI 共享文件完成后,有没有办法清理它并删除它?

Pat*_*ick 10

我自己解决了这个问题。应 Neil 的要求,我将其记录在此。

这是我从我的活动启动导出/备份的地方:

public class MyActivity {

    private void exportUserContent() {
        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("application/octet-stream");

        Uri uri = new FileProvider().getDatabaseURI(this);

        intent.putExtra(Intent.EXTRA_STREAM, uri);

        startActivity(Intent.createChooser(intent, "Backup via:"));
    }
    
}
Run Code Online (Sandbox Code Playgroud)

文件提供者:

public class FileProvider extends android.support.v4.content.FileProvider {

    public Uri getDatabaseURI(Context c) {
        // https://developer.android.com/reference/android/support/v4/content/FileProvider.html
        // old approach that worked until 2020-ish
        // File data = Environment.getDataDirectory();
        // String dbName = "UserContent.db";
        // String currentDBPath = "//data//com.url.myapp//databases//" + dbName;

        // File exportFile = new File(data, currentDBPath);
        File exportFile = c.getDatabasePath(dbName); // new approach

        return getFileUri(c, exportFile);
    }

    public Uri getFileUri(Context c, File f){
        return getUriForFile(c, "com.url.myapp.fileprovider", f);
    }

}
Run Code Online (Sandbox Code Playgroud)

在 AndroidManifest.xml 中:

<manifest ...>
    <application ...>
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.url.myapp.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>
Run Code Online (Sandbox Code Playgroud)

在 \app\src\main\res\xml\filepaths.xml 中(我认为第一个条目是相关的,但为了完整起见,我将包含整个文件):

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

    <files-path
        path=""
        name="migrations"/>

    <external-path
        path=""
        name="external"/>
</paths>
Run Code Online (Sandbox Code Playgroud)