如何使用 Uri 对象打开现有的 SQLite 数据库?

Ste*_* M. 6 database sqlite android uri

我用来Intent.ACTION_GET_CONTENT选择一个文件。

在该方法中onActivityResult我获取 Uri:

Uri selectedfile = data.getData();
Run Code Online (Sandbox Code Playgroud)

如何使用 Uri 对象selectedfile打开 SQLite 数据库?这不起作用:

SQLiteDatabase database = SQLiteDatabase.openDatabase(selectedfile.getPath(), null, SQLiteDatabase.OPEN_READONLY);
Run Code Online (Sandbox Code Playgroud)

Ant*_*ton 4

您不应该尝试从 Uri 打开 SQLiteDatabase。

SQLiteDatabase 被设计为直接与文件系统一起工作:它需要锁定和解锁数据库文件并管理单独的预写日志记录(-shm 和 -wal)文件,而 Uri 不一定引用 file:// 模式中的任何内容。相反,它可以是任何内容,从 https:// 或 content:// 到自定义应用程序定义的架构。

特别是自 Android 7.0 以来,Google 强制使用 content:// uri 在应用程序之间共享文件(这正是您的情况)。摘自这里:https ://developer.android.com/about/versions/nougat/android-7.0-changes.html :

对于面向 Android 7.0 的应用程序,Android 框架强制执行 StrictMode API 策略,禁止在应用程序外部公开 file:// URI。如果包含文件 URI 的意图离开您的应用程序,则应用程序将失败并出现 FileUriExposedException 异常。

要在应用程序之间共享文件,您应该发送 content:// URI 并授予对该 URI 的临时访问权限。授予此权限的最简单方法是使用 FileProvider 类。有关权限和共享文件的详细信息,请参阅共享文件。

相反,您应该获取 ContentResolver,从中打开 uri InputStream 并将流内容保存到本地临时文件,然后对其使用 SQLiteDatabase.openDatabase(filePath) :

void openDatabaseFromFileDialog() {
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
    startActivityForResult(Intent.createChooser(intent, "Select a database"), DB_FILE_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if(requestCode == DB_FILE_REQUEST && resultCode == RESULT_OK) {
        Uri dataUri= data.getData();
        openDatabaseFromUri(dataUri);
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

void openDatabaseFromUri(Uri uri) {
    InputStream inputStream = application.getContentResolver().openInputStream(uri);

    File file = File.createTempFile("sqlite", "");

    FileOutputStream outputStream = new FileOutputStream(file);
    byte[] buff = new byte[1024];
    int read;
    while ((read = inputStream.read(buff, 0, buff.length)) > 0)
        outputStream.write(buff, 0, read);
    inputStream.close();
    outputStream.close();

    openDatabaseFromFile(file.getPath());
}

void openDatabaseFromFile(String filePath) {
    // your code here
}
Run Code Online (Sandbox Code Playgroud)

另外,当您从另一个应用程序(可能是第三方)获取 SQLite 流时,我强烈建议您在单独的线程/AsyncTask/等中使用数据库。您永远不知道您收到了多少 PB :)