Экспорт и импорт базы данных SQLite - PullRequest
4 голосов
/ 02 июля 2019

Добрый вечер, я разрабатываю приложение для Android, у него есть база данных SQLite, мне нужен способ скопировать базу данных во внешнее хранилище устройства, где я могу скопировать на другое устройство, чтобы я мог импортировать базу данных в другое устройство.

Например:

Предположим, что приложение вызывает «пример», база данных находится в папке «/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 Простой экспорт и импорт базы данных SQLite на Android

Я уже вставил его в 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)

Ответы [ 2 ]

2 голосов
/ 02 июля 2019

Относительно вашей аварии:

  • Вы не создали каталог, по крайней мере, не через ваш код. Создайте объект File, указывающий на нужный каталог, затем вызовите mkdirs() для этого объекта.

  • Возможно, вы не обладаете разрешением WRITE_EXTERNAL_STORAGE, включая его запрос во время выполнения. Смотри https://developer.android.com/training/permissions/requesting.

Другие проблемы здесь включают:

  • У вас не будет возможности записать в запрошенное местоположение в Android Q (по умолчанию) и Android R (для всех приложений) . Я рекомендую вам написать в getExternalFilesDir() (находится на Context) или использовать Storage Access Framework.

  • Вы выполняете дисковый ввод-вывод в главном потоке приложения. Это приведет к зависанию вашего пользовательского интерфейса во время этого ввода-вывода. Пользователи могут подумать, что ваше приложение не работает. Есть много способов решения этой проблемы, хотя подход Jetpack будет использовать ViewModel и LiveData.

  • Вы не организуете индексирование файла с помощью MediaStore, поэтому пользователь не сможет увидеть его в своем файловом менеджере на рабочем столе. Используйте MediaScannerConnection.scanFile() для индексации файла.

  • "/data/data/com.gnd.example/databases/dados.db" - неправильный путь на многих устройствах Android. Никогда не указывайте жесткие пути. Используйте getDatabasePath() на Context, чтобы получить путь к вашей базе данных.

В этом примере Java-приложения показано, как это делается, в контексте текстового редактора вместо решения для резервного копирования базы данных.

В общем, я рекомендую немного отложить этот проект и прочитать последнюю книгу о разработке приложений для Android. Большинство проблем, о которых я здесь упоминаю, связано с темами, которые будут рассмотрены в достойной книге по Android.

2 голосов
/ 02 июля 2019

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 ();
}

создаст каталоги, если они не существуют (при условии, что права доступа правильные)

Рабочий пример: -

TheНиже приведено рабочее приложение

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> 
  • Обратите внимание на <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> (для более ранних устройств)

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) {

    }
}
  • A Оченьбазовая пустая база данных (исключение android_metadata table), достаточно для проверки резервной копии.

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, что исключает необходимость резервного копирования дополнительных файлов).

Result

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...