【问题标题】:Export and import SQLite database导出和导入 SQLite 数据库
【发布时间】:2019-11-12 13:05:37
【问题描述】:

晚上好,我正在开发一个 android 应用程序,它有 SQLite 数据库,我需要一种方法将数据库复制到设备的外部存储,在那里我可以复制到另一个设备,因此我可以将数据库导入到另一台设备。

例如:

假设应用程序调用“example”,数据库在“/data/data/com.gnd.example/databases”文件夹中,名为data.db,需要复制到“example/backup”文件夹”,例如“/storage/emulated/0/Example/Backup”。这是第一部分。

第二部分是导入,应用程序应该将文件从“example / import”文件夹复制到文件夹“/data/data/com.gnd.example/databases”

为此,我有两个按钮活动,btn_export 和 btn_import。

我已经依赖以下解决方案:

导入/导出到 android sqlite 数据库 Android上SQLite数据库的简单导出和导入

我已经在 AndroidManifest 中插入了它

如何向用户请求权限?

我尝试使用我在其中一个示例中获取的这段代码进行复制

private void backupDatabase () throws IOException {
   String inFileName = "/data/data/com.gnd.example/databases/dados.db";
   File dbFile = new File (inFileName);
   FileInputStream fis = new FileInputStream (dbFile);

   String outFileName = Environment.getExternalStorageDirectory () + "/ example / backup / data.db";
   OutputStream output = new FileOutputStream (outFileName);
   byte [] buffer = new byte [1024];
   int length;
   while ((length = fis.read (buffer))> 0) {
       output.write (buffer, 0, length);
   }
   output.flush ();
   output.close ();
   fis.close ();

}

按钮如下所示:

   @Override
   public void onClick (View view) {
       try {
           backupDatabase ();
       } catch (IOException e1) {
           e1.printStackTrace ();
       }
   
});

按下按钮时记录:

07/01 19:35:39: Launching app
$ adb shell am start -n "com.gnd.keepkey / com.gnd.keepkey.Telephone" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Client not ready yet..Waiting for process to come online
Waiting for process to come online
Connected to process 27724 on device motorola-moto_z2_play-0039635857
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I / zygote: Do partial code cache collection, code = 20KB, data = 29KB
I / zygote: After code cache collection, code = 20KB, data = 29KB
    Increasing code cache capacity to 128KB
I / zygote: Do partial code cache collection, code = 20KB, data = 47KB
I / zygote: After code cache collection, code = 20KB, data = 47KB
    Increasing code cache capacity to 256KB
I / zygote: Compiler allocated 4MB to compile void android.widget.TextView. <Init> (android.content.Context, android.util.AttributeSet, int, int)
I / zygote: Full code cache collection, code = 120KB, data = 82KB
I / zygote: After code cache collection, code = 117KB, data = 62KB
I / zygote: Do partial code cache collection, code = 125KB, date = 79KB
I / zygote: After code cache collection, code = 125KB, data = 79KB
    Increasing code cache capacity to 512KB
W / System.err: java.io.FileNotFoundException: /storage/emulated/0/teste/dados.db (No such file or directory)
        at java.io.FileOutputStream.open0 (Native Method)
W / System.err: at java.io.FileOutputStream.open (FileOutputStream.java:287)
        at java.io.FileOutputStream. <init> (FileOutputStream.java:223)
        at java.io.FileOutputStream. <init> (FileOutputStream.java:110)
        at com.gnd.keepkey.funcoes.Exportar_Importar.backupDatabase (Export_Importar.java:87)
        at com.gnd.keepkey.funcoes.Exportar_Importar.access $ 000 (Export_Importar.java:42)
        at com.gnd.keepkey.funcoes.Export_Import $ 1.onClick (Export_Import.java:69)
W / System.err: at android.view.View.performClick (View.java:6259)
        at android.view.View $ PerformClick.run (View.java:24732)
        at android.os.Handler.handleCallback (Handler.java:789)
        at android.os.Handler.dispatchMessage (Handler.java:98)
        at android.os.Looper.loop (Looper.java:164)
        at android.app.ActivityThread.main (ActivityThread.java:6592)
        at java.lang.reflect.Method.invoke (Native Method)
W / System.err: at com.android.internal.os.Zygote $ MethodAndArgsCaller.run (Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:769)

【问题讨论】:

  • “因为不是所有的设备都有一个”——每个 Android 设备都有 Android SDK 所指的external storage。 “我可以将它复制到另一台设备,因此您可以将数据库导入另一台设备的应用程序”——用户无权访问 Android SDK 所指的internal storage。除此之外,尚不清楚您的问题是什么。您可能想添加 minimal reproducible example 并解释发生了什么问题。
  • 这样更好吗?抱歉英文有误,我正在使用翻译,我是这个存储问题的初学者,所以这个困难和这些关于内部和外部的错误。

标签: java android database sqlite


【解决方案1】:

FileNotFoundException 可能是由于目录 teste 不存在,可能是由于权限。

使用:-

private void backupDatabase () throws IOException {
    String inFileName = "/data/data/com.gnd.example/databases/dados.db";
    File dbFile = new File (inFileName);
    FileInputStream fis = new FileInputStream (dbFile);

    String outFileName = Environment.getExternalStorageDirectory () + "/ example / backup / data.db";
    //<<<<<<<<<<< CODE ADDED >>>>>>>>>>
    File os = new File(outFileName); 
    if (!os.getParentFile().exists()) {
        os.getParentFile().mkdirs();
    }
    //<<<<<<<<<< END Of ADDED CODE >>>>>>>>>>
    OutputStream output = new FileOutputStream(os); //<<<<<<<<<< CHANGED
    byte [] buffer = new byte [1024];
    int length;
    while ((length = fis.read (buffer))> 0) {
        output.write (buffer, 0, length);
    }
    output.flush ();
    output.close ();
    fis.close ();
}

如果目录不存在则创建目录(假设权限正确)

工作示例:-

以下是一个工作应用程序

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="aso.so56843045backup">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest> 
  • 注意&lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/&gt;(适用于早期设备)

ExternalStoragePermissions.java

class ExternalStoragePermissions {

    public int API_VERSION = Build.VERSION.SDK_INT;
    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {

            //Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };
    public static final String THISCLASS = ExternalStoragePermissions.class.getSimpleName();
    private static final String LOGTAG = "SW_ESP";

    public ExternalStoragePermissions() {}
    // Note call this method
    public static void verifyStoragePermissions(Activity activity) {
        int permission = ActivityCompat.checkSelfPermission(
                activity,
                Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if(permission != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                    activity,
                    PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE
            );
        }
    }
}
  • 如果未授予权限,则无法创建目录,从而导致 FileNotFoundException。

DBHelper.java

public class DBHelper extends SQLiteOpenHelper {

    public static final String DBNAME = "dados.db";
    public static final int DBVERSION = 1;

    public DBHelper(Context context) {
        super(context, DBNAME, null, DBVERSION);
        this.getWritableDatabase();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}
  • 一个非常基本的空数据库(android_metadata 表除外),足以检查备份。

MainActivity.java

public class MainActivity extends AppCompatActivity {

    DBHelper mDBHlpr;
    Button mBackup;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBackup = this.findViewById(R.id.backup);
        mBackup.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mDBHlpr.close();
                try {
                    backupDatabase();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        ExternalStoragePermissions.verifyStoragePermissions(this);
        mDBHlpr = new DBHelper(this);
    }

    private void backupDatabase () throws IOException {
        FileInputStream fis = new FileInputStream (this.getDatabasePath("dados.db").getPath());
        String outFileName = Environment.getExternalStorageDirectory () + "/example/backup/" + String.valueOf(System.currentTimeMillis()) + "data.db";
        Log.d("OSFILEPATH",outFileName);
        File os = new File(outFileName);
        if (!os.getParentFile().exists()) {
            os.getParentFile().mkdirs();
        }
        OutputStream output = new FileOutputStream(os);
        byte [] buffer = new byte [1024];
        int length;
        while ((length = fis.read (buffer))> 0) {
            output.write (buffer, 0, length);
        }
        output.flush ();
        output.close ();
        fis.close ();
    }
}

注意事项

  • 安装后首次运行时,将为以后的设备请求权限(单击允许)。

  • 已使用时间戳命名备份,因此可以存在多个备份。

  • 数据库已关闭,(这应该与 Android Pie+ 对应,这里默认为 WAL 模式,关闭时应清空(提交更改)-wal 和 -shm 文件,从而无需备份其他文件) .

结果

【讨论】:

  • ExternalStoragePermissions.java 这是该代码的唯一类吗?
  • @GabrielDelfino 是的,它是 uniqiue,但您应该使用它或类似的东西,您必须授予应用程序使用 EXTERNALSTORAGE 的权限。如果不是,您将无法创建目录并且会遇到 FileNotFoundException。我建议将应用程序复制为一个新项目。让它运行,然后将其合并到您的应用程序中。
  • 非常感谢,它运行良好。要将其导入另一台设备,只需将路径颠倒过来即可?
  • @GabrielDelfino 基本上是的,尽管数据库的状态可能会有点复杂(我通过在恢复后重新启动应用程序来解决这个问题)。您能否在其中一个答案上打勾,以表明该问题已得到解决。
  • 应用安装后如何创建文件夹?
【解决方案2】:

关于你的崩溃:

这里的其他问题包括:

  • 您将无法写入您请求的位置in Android Q (by default) and Android R (for all apps)。我建议您写信给getExternalFilesDir()(在Context 上找到)或使用存储访问框架。

  • 您正在主应用程序线程上执行磁盘 I/O。这将导致您的 UI 在 I/O 发生时冻结。用户可能会认为您的应用程序已损坏。有很多方法可以解决这个问题,尽管 Jetpack 方法是使用 ViewModelLiveData

  • 您没有安排让您的文件被MediaStore 索引,因此用户将无法在他们的桌面文件管理器中看到它。使用MediaScannerConnection.scanFile() 索引文件。

  • "/data/data/com.gnd.example/databases/dados.db" 是许多 Android 设备上的错误路径。永远不要硬编码路径。在Context 上使用getDatabasePath() 获取数据库的路径。

This sample Java app 展示了如何在文本编辑器而不是数据库备份解决方案的上下文中完成大部分工作。

一般来说,我建议您将这个项目搁置一段时间,并阅读有关 Android 应用程序开发的最新书籍。我在这里提到的大多数问题都与一本体面的 Android 书籍中涉及的主题有关。

【讨论】:

  • 我理解你的观点,但我缺少的只是完成这个项目。我应该如何使用getDatabasePath()?我看了但没看懂。
  • 我请求了许可并接受了,之后当我点击按钮时,日志没有显示任何新内容,没有更新。
  • 如果我显示代码解释会很有帮助。
  • @GabrielDelfino:“我应该如何使用 getDatabasePath ()?” -- 打电话给getDatabasePath() on a Context,比如你的Activity。将您的数据库名称 (dados.db) 传递给它。它将返回一个指向您的数据库文件的File 对象。请注意,根据您的数据库的设置方式,可能需要复制三个文件,而不是一个 - 在 Android Studio 的设备文件资源管理器中检查您的数据库目录以查看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-08
  • 2011-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-10
相关资源
最近更新 更多