Копирование базы данных внутри фрагмента, затем ее использование в Android Studio

#android #sqlite

#Android #sqlite

Вопрос:

Код из моего класса databasehelper.

 package com.app.testapp;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class DatabaseHelper extends SQLiteOpenHelper {

    private String DB_PATH;
    private static String DB_NAME = "test";
    private final Context context;

    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, 1);
        this.context = context;
        this.DB_PATH = "/data/data/"   context.getPackageName()   "/"   "databases/";
        Log.e("Database Path:", DB_PATH);
    }

    public void copyDatabase() throws IOException {
        InputStream myInput = context.getAssets().open(DB_NAME);
        String outString = DB_PATH   DB_NAME;
        OutputStream myOutput = new FileOutputStream(outString);
        byte[] buffer = new byte[10];
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }
        myOutput.flush();
        myOutput.close();
        myInput.close();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

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

Код из моего класса viewFragment

 package com.app.testapp;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.io.IOException;

public class ViewFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment, container, false);
        DatabaseHelper db = new DatabaseHelper(getActivity());
        initDatabase(db);
        return view;
    }

    private void initDatabase(DatabaseHelper db) {
        try {
            db.copyDatabase();
        } catch (IOException ioe) {
            throw new Error("Error creating database");
        }
    }

}
  

Код из моего класса MainActivity.

 package com.app.testapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getSupportFragmentManager().beginTransaction().replace(R.id.frag_container, new ViewFragment()).commit();
    }
}
  

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

Моя ошибка заключается в следующем

 2019-03-17 23:32:46.645 13197-13197/com.app.testapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.app.testapp, PID: 13197
java.lang.Error: Error creating database
    at com.app.testapp.ViewFragment.initDatabase(ViewFragment.java:28)
    at com.app.testapp.ViewFragment.onCreateView(ViewFragment.java:20)
    at android.support.v4.app.Fragment.performCreateView(Fragment.java:2439)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1460)
    at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
    at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:802)
    at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
    at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
    at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
    at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3273)
    at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3229)
    at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
    at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:620)
    at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
    at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1248)
    at android.app.Activity.performStart(Activity.java:6679)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2609)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
    at android.app.ActivityThread.-wrap12(ActivityThread.java)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6077)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
  

Дополнительный журнал :-

     E/Database Path:: /data/data/com.app.testapp/databases/
W/System.err: java.io.FileNotFoundException: /data/data/com.app.testapp/databases/test (No such file or directory)
W/System.err:     at java.io.FileOutputStream.open(Native Method)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:221)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:108)
        at com.app.testapp.DatabaseHelper.copyDatabase(DatabaseHelper.java:29)
        at com.app.testapp.ViewFragment.initDatabase(ViewFragment.java:26)
        at com.app.testapp.ViewFragment.onCreateView(ViewFragment.java:20)
        at android.support.v4.app.Fragment.performCreateView(Fragment.java:2439)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1460)
        at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
        at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:802)
        at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
        at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
        at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
        at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3273)
        at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3229)
        at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
W/System.err:     at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:620)
        at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1248)
        at android.app.Activity.performStart(Activity.java:6679)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2609)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6077)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
  

Комментарии:

1. Я мог бы догадаться о проблеме, НО если вы удалите приложение и добавите строку ioe.printStackTrace() перед строкой throw new Error("Error creating database"); , а затем запустите повторно, а затем отредактируйте свой вопрос, чтобы включить эту трассировку стека. Не нужно было бы делать предположение, поскольку было бы показано базовое исключение, а не неинформативная ошибка создания базы данных .

2. я думаю, что я только что добавил то, что вы просили, иначе я не уверен

3. Как и предполагалось. Отвечайте по ходу дела.

Ответ №1:

В вашем коде есть несколько проблем.

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

Во-вторых, будет предпринята попытка скопировать базу данных при каждом запуске приложения, поскольку нет проверки, существует ли база данных на самом деле.

Хотя на самом деле это не проблема, если используется метод getDatabasePath контекста, нет необходимости жестко кодировать путь, который может быть неправильным. Это также должно быть проверено на будущее.

Я полагаю, что следующий DatabaseHelper поможет решить ваши проблемы (за исключением случаев, когда проблема заключается в том, что файл test был скопирован в папку assets (и является допустимой базой данных)) :-

DatabaseHelper.java

 public class DatabaseHelper extends SQLiteOpenHelper {

    private String DB_PATH;
    private static String DB_NAME = "test";
    private final Context context;

    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, 1);
        this.context = context;
        this.DB_PATH = context.getDatabasePath(DB_NAME).getPath(); //<<<<<<<<<< REPLACES following line
        //this.DB_PATH = "/data/data/"   context.getPackageName()   "/"   "databases/";
        Log.e("Database Path:", DB_PATH);
        if (!checkDatabase()) {
            try {
                copyDatabase();
            } catch (IOException ioe) {
                ioe.printStackTrace();
                throw  new RuntimeException("Error creating Database - see stack-trace above.");
            }
        }
    }

    private boolean checkDatabase() {
        File dbpath = new File(DB_PATH);
        if (dbpath.exists()) {
            return true;
        } else {
            if (!dbpath.getParentFile().exists()) { 
                dbpath.getParentFile().mkdirs();
            }
        }
        return false;
    }

    private void copyDatabase() throws IOException { // MADE PRIVATE AS USED INTERNALLY
        InputStream myInput = context.getAssets().open(DB_NAME);
        String outString = DB_PATH;
        OutputStream myOutput = new FileOutputStream(outString);
        byte[] buffer = new byte[10];
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }
        myOutput.flush();
        myOutput.close();
        myInput.close();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}
  
  • В основном добавление метода checkDatabase, который создаст папку ** databases *, если она не существует, является решением проблемы, с которой вы столкнулись.

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

  • Обратите внимание, что простое создание экземпляра DatabaseHelper при необходимости выполнит копирование файла базы данных из папки assets, поэтому ваш код во фрагменте может быть

:-

 public class ViewFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment, container, false);
        DatabaseHelper db = new DatabaseHelper(getActivity());
        return view;
    }
}