SQLite читает базу из активов после очистки кеша - PullRequest
0 голосов
/ 01 декабря 2018

У меня есть база данных Sqlite, которую я читаю из файла активов.Это был вопрос о том, как обойти один момент.Все работает хорошо, но если вы зайдете в настройки -> приложение -> и очистите кеш и данные, базы данных и т. Д., То приложение выдает исключение SQLiteException без такой таблицы: bus_end (код 1): при компиляции: SELECT * FROM потому чтобаза перезаписывается.

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

Вот мой код:

    public class DatabaseHelper extends SQLiteOpenHelper {

//The Android's default system path of your application database.
private static String DB_PATH = "";
private static final int DB_VERSION = 1;

private static String DB_NAME = "app_database.db";

private final Context myContext;

private SQLiteDatabase myDataBase;

public DatabaseHelper(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
    this.myContext = context;

}

/**
 *
 * Создает пустую базу данных в системе и переписывает ее с помощью собственной базы данных.
 */
public void createDataBase() throws IOException {
    boolean dbExist = checkDataBase();
    if (!dbExist)
        this.getWritableDatabase();//Создает и/или открывает базу данных
        if (myDataBase.isOpen()){
            myDataBase.close();
        }
        try {
            copyDataBase();
        } catch (IOException e) {
            throw new Error(e + "Error copying database");

    }
}

/**
 * Check if the database already exist to avoid re-copying the file each time you open the application.
 *
 * @return true if it exists, false if it doesn't
 */
private boolean checkDataBase() {
    SQLiteDatabase checkDB = null;
    String path = DB_PATH + DB_NAME;
    try {
        checkDB = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
    } catch (SQLiteException e) {
        //database does't exist yet
    }
    if (checkDB != null) {
        checkDB.close();
    }

    return checkDB != null;
}

/**
 * Copies your database from your local assets-folder to the just created empty database in the
 * system folder, from where it can be accessed and handled.
 * This is done by transfering bytestream.
 */
private void copyDataBase() throws IOException { //Откройте локальный db в качестве входного потока
    InputStream myInput = myContext.getAssets().open(DB_NAME); //Путь к только что созданному пустому db
    String outFileName = DB_PATH + DB_NAME; //Откройте пустой бит в качестве выходного потока
    OutputStream myOutput = new FileOutputStream(outFileName);
    //передавать байты из входного файла в выходной файл
    byte[] buffer = new byte[1024];
    int length;
    while ((length = myInput.read(buffer)) > 0) {
        myOutput.write(buffer, 0, length);
    }
    //Закрыть потоки
    myOutput.flush();
    myOutput.close();
    myInput.close();
}

public void openDataBase() throws SQLException {
    String myPath = DB_PATH;
    myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}


//  ПОЛУЧИТЬ
public Cursor getAllData(String table_name) {
    myDataBase = getReadableDatabase();
    return myDataBase.query(table_name, null, null, null, null, null, null);
    }



@Override
public synchronized void close() {
    if (myDataBase != null)
        myDataBase.close();
    super.close();
}

@Override
public void onCreate(SQLiteDatabase db) {

}

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

И ФРАГМЕНТ

private void retrieve() {
listStationItemList.clear();
try {
    db.createDataBase();
    db.openDataBase();
} catch (Exception e) {
    e.printStackTrace();
}

//RETRIEVE
Cursor cursor = db.getAllData(TABLE_BUS);
//LOOP AND ADD TO ARRAYLIST
while (cursor.moveToNext()) {
    long _id = cursor.getLong(0);
    String name = cursor.getString(1);
    String details = cursor.getString(2);

    ItemListStation p = new ItemListStation(name, details);
    listStationItemList.add(p);
}
//CHECK IF ARRAYLIST ISNT EMPTY
if (!(listStationItemList.size() < 1)) {
    recyclerView.setAdapter(adapter);
}
db.close();

}

Ответы [ 2 ]

0 голосов
/ 01 декабря 2018

Я думаю, что у вас есть ряд проблем с процессом создания / копирования;

  • Переменная myDataBase будет иметь значение null.
  • Переменная DB_PATHустанавливается на "", таким образом, копирование завершается сбоем из-за того, что оно доступно только для чтения, за исключением того, что в журнале имеется аналог (примечание 2-я строка, также обратите внимание на измененный код, чтобы не перехватывать такое исключение, и игнорировать его, поэтому таблица не найденановая БД пуста, не перезаписывается)

: -

2018-12-02 02:50:25.525 5780-5780/so53569158.so53569158fragmentlistview D/DBOUTFILE: Outfilename (the database file to be written to) is set to app_database.db
2018-12-02 02:50:25.525 5780-5780/so53569158.so53569158fragmentlistview D/AndroidRuntime: Shutting down VM
2018-12-02 02:50:25.527 5780-5780/so53569158.so53569158fragmentlistview E/AndroidRuntime: FATAL EXCEPTION: main
    Process: so53569158.so53569158fragmentlistview, PID: 5780
    java.lang.Error: java.io.FileNotFoundException: app_database.db (Read-only file system)Error copying database
        at so53569158.so53569158fragmentlistview.DatabaseHelper.createDatabaseAlt(DatabaseHelper.java:59)
        at so53569158.so53569158fragmentlistview.DatabaseHelper.<init>(DatabaseHelper.java:29)
        at so53569158.so53569158fragmentlistview.MainActivity.onCreate(MainActivity.java:40)
        at android.app.Activity.performCreate(Activity.java:6975)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

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

Если его нет, он также проверяет наличие родительского каталога и создает его, если он не существует.

  • Каталог не существует, когда нетбазы данных были созданы.
  • Я полагаю, что использование checkDB = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY); было для преодоления несуществующего каталога и может быть проблематичным.
  • Существует потенциальная ошибка при простой проверке файла, в том, что есть потенциал для этогоне быть файлом базы данных (дополнительную проверку можно выполнить, открыв для нее проверку первых 16 байтов, которые должны соответствовать данным заголовка SQLite).

В любом случае, вот код, который работает

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

1.Код фрагмента (использовался MainFragment): -

открытый класс MainFragment extends Fragment {

DatabaseHelper db;
String TABLE_BUS = "bus_end";


public static MainFragment newInstance() {
    return new MainFragment();
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_mis_entradas_local, container, false);
    db = new DatabaseHelper(getContext()); //<<<<<<<<<< Assumed you had this
    retrieve();                            //<<<<<<<<<< Assumed you had this
    return view;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
}

private void retrieve() {

    //listStationItemList.clear(); // Commented out for convenience

    /**
     *  Block commeneted out NOT NEEDED WITH NEW METHODOLOGY
      */
    /*
    try {
        db.createDataBase();
        db.openDataBase();
    } catch (Exception e) {
        e.printStackTrace();
    }
    */

    //RETRIEVE
    Cursor cursor = db.getAllData(TABLE_BUS);
    //LOOP AND ADD TO ARRAYLIST
    while (cursor.moveToNext()) {
        long _id = cursor.getLong(0);
        String name = cursor.getString(1);
        String details = cursor.getString(2);

        //ItemListStation p = new ItemListStation(name, details); //<<<<<<<<<< Commented out for convenience
        //listStationItemList.add(p); //<<<<<<<<<< Commented out for convenience
    }
    /**
     * Block below commented out for convenience
     */
    /*
    //CHECK IF ARRAYLIST ISNT EMPTY
    if (!(listStationItemList.size() < 1)) {
        recyclerView.setAdapter(adapter);
    */
    db.close();
}

}

  • Обратите внимание, что он напрямую не пытается вызвать createDatabase , а затем openDatabase , это делается при создании DatabaseHelper .Следовательно, почему блок кода был закомментирован.

2.DatabaseHelper.java

public class DatabaseHelper extends SQLiteOpenHelper {

    //The Android's default system path of your application database.
    private static String DB_PATH = "";
    private static final int DB_VERSION = 1;

    private static String DB_NAME = "app_database.db";

    private final Context myContext;

    private SQLiteDatabase myDataBase;

    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        this.myContext = context;
        createDatabaseAlt();
        myDataBase = this.getWritableDatabase();
    }

    /**
     * NOTE NOT USED SEE createDatabaseAlt below (it's replacment)
     * Создает пустую базу данных в системе и переписывает ее с помощью собственной базы данных.
     */
    public void createDataBase() throws IOException {
        boolean dbExist = checkDataBase();
        if (!dbExist) {
            this.getWritableDatabase(); //<<<<<<<<<< myDatabase will be null
            myDataBase = this.getWritableDatabase();//Создает и/или открывает базу данных
        }
        if (myDataBase.isOpen()){
            myDataBase.close();
        }
        try {
            copyDataBase(); //<<<<<<<<<< Copy fails due to app_database.db (Read-only file system)Error copying database
        } catch (IOException e) {
            throw new Error(e + "Error copying database");
        }
    }

    // Alternative Create Database
    public void createDatabaseAlt() {
        if (!checkDB()) {
            try {
                copyDataBase();
            } catch (IOException e) {
                throw new Error(e + "Error copying database");
            }
        }
    }

    /**
     * Check if the database already exist to avoid re-copying the file each time you open the application.
     *
     * @return true if it exists, false if it doesn't
     */
    private boolean checkDataBase() {
        SQLiteDatabase checkDB = null;
        String path = DB_PATH + DB_NAME;
        try {
            checkDB = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
        } catch (SQLiteException e) {
            //database does't exist yet
        }
        if (checkDB != null) {
            checkDB.close();
        }

        return checkDB != null;
    }

    // Alternative Check that doesn't open the database to create the directory
    private boolean checkDB() {
        File db = new File(myContext.getDatabasePath(DB_NAME).getPath());
        if (db.exists()) return true;
        File dbdir = new File(db.getParent());
        if (!dbdir.exists()) {
            dbdir.mkdirs();
        }
        return false;
    }


    /**
     * Copies your database from your local assets-folder to the just created empty database in the
     * system folder, from where it can be accessed and handled.
     * This is done by transfering bytestream.
     */
    private void copyDataBase() throws IOException { //Откройте локальный db в качестве входного потока
        InputStream myInput = myContext.getAssets().open(DB_NAME); //Путь к только что созданному пустому db

        //String outFileName = DB_PATH + DB_NAME; //Откройте пустой бит в качестве выходного потока
        OutputStream myOutput = new FileOutputStream(myContext.getDatabasePath(DB_NAME).getPath());
        //передавать байты из входного файла в выходной файл
        byte[] buffer = new byte[1024];
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }
        //Закрыть потоки
        myOutput.flush();
        myOutput.close();
        myInput.close();
    }

    /**
     * Not used NOTE this open database as read-only and thus was the cause of one of the issues
     */
    public void openDataBase() throws SQLException {
        String myPath = DB_PATH;
        myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
    }


    //  ПОЛУЧИТЬ
    public Cursor getAllData(String table_name) {
        myDataBase = getReadableDatabase();
        return myDataBase.query(table_name, null, null, null, null, null, null);
    }



    @Override
    public synchronized void close() {
        if (myDataBase != null)
            myDataBase.close();
        super.close();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}
  • Обратите внимание, что старый / избыточный код оставлен и прокомментирован при необходимости
  • Пожалуйста, обратитесь к комментариям
0 голосов
/ 01 декабря 2018

Эта проблема может быть решена двумя способами:

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

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

     <application
      android:manageSpaceActivity="your activity" />
    

Я рекомендую использовать второй метод.

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