sqlite db не найден на мобильном устройстве, но работает на эмуляторе - PullRequest
0 голосов
/ 09 июня 2018

Я создал базу данных sqlite, состоящую из таблицы, и база данных содержит около 2000 строк.

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

Но когда я тестирую его на мобильном устройстве, он выдает следующую ошибку:

android.database.sqlite.SQLiteException: нет такой таблицы: person (код 1):,во время компиляции: ВЫБЕРИТЕ имя от лица, ГДЕ rowid = 292

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

Я просмотрел прошлые проблемы, и они не соответствуют моей проблеме.

  1. Я не установил свой собственный путь для места хранения БД.

    Когда я посмотрел в Device File Explorer, путь к базе данных был /data/data/com.somepackage.myappname/databases/person

  2. Я попытался удалить приложение и заново установить его, но это не имеет значения.

Сведения о настройке sdk, если применимо.

minSdkVersion 16

targetSdkVersion 27

Мобильное устройство: с использованием версии Android 8.0.0

Посоветуйте, пожалуйста, как можно, чтобы база данных автоматически поставлялась с приложением при установке(когда я нажимаю запустить на Android Studio).

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

    try {
        SQLiteDatabase database = this.openOrCreateDatabase("person", MODE_PRIVATE, null);
        database.execSQL("DROP TABLE IF EXISTS person");
        database.execSQL("CREATE TABLE IF NOT EXISTS person (name VARCHAR, name_size INT(8))");

        BufferedReader br = new BufferedReader(new InputStreamReader(getAssets().open("person.txt")));
        String line;
        while ((line = br.readLine()) != null) {
            String sql = "INSERT INTO person (name, name_size) VALUES ('" + line + "', " + line.length() + ")";
            database.execSQL(sql);
        }database.close();
    }
    catch (Exception e){
        e.printStackTrace();
    }  

Я инициализирую базу данных методом onCreate.

database = this.openOrCreateDatabase("person", MODE_PRIVATE, null);

При нажатии кнопки выполняется следующий метод.Ошибка возникает в первой строке в этом методе.

private String retrieveNextPerson(int randomIndex){
        //error on this raw query
        Cursor cursor = database.rawQuery("SELECT name from person WHERE rowid = " + randomIndex, null);
        int wordIndex = cursor.getColumnIndex("name");
        cursor.moveToFirst();
        return cursor.getString(wordIndex);
    }

1 Ответ

0 голосов
/ 10 июня 2018

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

  • дляРаспространение предварительно заполненной базы данных включает

    • a) заполнение базы данных (, как правило, с использованием инструмента управления SQLite ),
    • b) копирование этого (файл в виде базы данных ) в папку ресурсов, а затем: -
    • c) извлечение этого файла из папки ресурсов.

      • с использованием SQLiteAssetHelper упрощает эту задачу, , отмечая, что при SQLiteAssethelper файл БД должен существовать в папке баз данных (вам, скорее всего, потребуется создать это) ).

тогда я подозреваю, что вы преждевременно вызываете: -

 database = this.openOrCreateDatabase("person", MODE_PRIVATE, null);

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

Вам необходимо загрузить данные перед использованием вышеуказанной строки.

Альтернативно, если вы добавили следующий код сразу после database = this.openOrCreateDatabase("person", MODE_PRIVATE, null);

Cursor csr = database.query("sqlite_master",null,"name='person'",null,null,null,null);
if (csr.getCount() < 1) {
    database.execSQL("CREATE TABLE IF NOT EXISTS person (name VARCHAR, name_size INT(8))");
........ rest of the code that inserts data from the person.txt asset
}

Таблица будет создана.Тем не менее, он будет пустым (вы можете скопировать данные из ресурса person.txt здесь)

Дополнительный комментарий: -

Спасибо за ответ.Не уверен, что вы пропустили ту часть, где я упоминал, что создание базы данных уже сделано, и эта часть была закомментирована.Я однажды вызвал создание БД и загрузил его с данными, а БД сейчас просто сидит в приложении (по крайней мере, для эмулятора).Упомянутая вами инициализация должна открыть существующую базу данных с существующей таблицей, поэтому я не понимаю, почему это было бы преждевременно.

Ниже приведена довольно солидная переделка вашего кода, которая будет обслуживать то, что яПоверьте, это может быть наиболее вероятными причинами: -

public class MainActivity extends AppCompatActivity {

    public static final String DBNAME = "person";
    public static final String TBNAME = "person";
    public static final String COL_NAME = "name";
    public static final String COL_NAME_SIZE = "name_size";
    public static final String ASSET_FILENAME = "person.txt";

    public static final String SQLITE_MASTER_TABLE = "sqlite_master";
    public static final String COL_SQLITE_MATSER_NAME = "name";

    static final int MIMINUM_ROWS_IN_PERSONTABLE = 1;

    SQLiteDatabase db;
    BufferedReader br;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        checkPersonTable(); // Notes sets db either way

        // FOR TESTING
        long rows_in_person_table = DatabaseUtils.queryNumEntries(db,TBNAME);
        Log.d(
                "PERSON ROW COUNT",
                "The number of rows in the " +
                        TBNAME +
                        " table is " +
                        String.valueOf(rows_in_person_table)
        );
    }


    private void checkPersonTable() {

        db = this.openOrCreateDatabase(DBNAME, Context.MODE_PRIVATE,null);
        // Database will now exist but it may or may not contain the person table so check sqlite_master
        Cursor csr = db.query(
                SQLITE_MASTER_TABLE,
                new String[]{COL_SQLITE_MATSER_NAME},
                        COL_SQLITE_MATSER_NAME + "=?",
                        new String[]{TBNAME},
                        null,null,null
        );
        // Cursor will contain 1 row if the person table exists so check count
        int person_table_count = csr.getCount();
        csr.close();
        // Before attemtping to create the Person table ensure that the assets file exists
        // If not then throw a RunTime exception

        if (person_table_count < 1) {
            try {
                if (!Arrays.asList(getResources().getAssets().list("")).contains(ASSET_FILENAME)) {
                    StringBuilder sb = new StringBuilder();
                    throw new RuntimeException("Asset file " +
                            ASSET_FILENAME +
                            " not found in the assets folder." +
                            " The following assets were found" +
                            sb
                    );
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (person_table_count < 1) {
            db.execSQL("CREATE TABLE IF NOT EXISTS " + TBNAME +
                    "(" +
                    COL_NAME + " TEXT," +
                    COL_NAME_SIZE + " INTEGER" +
                    ")"
            );
            loadPersonsFromAssets();
        } else {
            // <<<<<<<<<< NOTE Optional will load data from assets if miminum nuber of rows
            //                 aren't in the person table
            if (DatabaseUtils.queryNumEntries(db,TBNAME) < MIMINUM_ROWS_IN_PERSONTABLE) {
                loadPersonsFromAssets();
            }
        }
    }

    // Load the person table from the Assets File
    private void loadPersonsFromAssets() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(getAssets().open(ASSET_FILENAME)));
            String line, sql;
            int lines_read = 0;
            db.beginTransaction();
            while ((line = br.readLine()) != null) {
                sql = "INSERT INTO " + TBNAME + " VALUES('" + line + "'," + String.valueOf(line.length()) + ")";
                db.execSQL(sql);
                lines_read++;
            }
            db.setTransactionSuccessful();
            db.endTransaction();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Asset File " + ASSET_FILENAME + " not found in the assets folder.");
        }
    }
}

Это будет: -

  • попытаться открыть базу данных только один раз, установив объект SQliteDatabase, когда это произойдет.
  • создаст базу данных person , если она не существует, а затем создаст таблицу person.
  • Даже если база данных существует, она продолжает проверять, существует ли таблицаи создаст его, если этого не произойдет.
  • Исключением является случай, если файл ресурса не существует в папке ресурсов, и в этом случае будет сгенерировано исключение времени выполнения (как если бы этого не было,что-то радикально неправильное, так как оно всегда должно существовать как часть пакета)в нескольких строках (в зависимости от значения MIMINUM_ROWS_IN_PERSONTABLE ).

Если файл активов person.txt не существует, вы получите исключение, аналогичное: -

06-10 03:58:43.503 3097-3097/personthing.so50777840 E/AndroidRuntime: FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{personthing.so50777840/personthing.so50777840.MainActivity}: java.lang.RuntimeException: Asset file person.txt not found in the assets folder. The following assets were found
         found asset file :- images
         found asset file :- notperson.txt
         found asset file :- sounds
         found asset file :- webkit
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
        at android.app.ActivityThread.access$600(ActivityThread.java:130)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4745)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
        at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.RuntimeException: Asset file person.txt not found in the assets folder. The following assets were found
         found asset file :- images
         found asset file :- notperson.txt
         found asset file :- sounds
         found asset file :- webkit
        at personthing.so50777840.MainActivity.checkPersonTable(MainActivity.java:83)
        at personthing.so50777840.MainActivity.onCreate(MainActivity.java:39)
        at android.app.Activity.performCreate(Activity.java:5008)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
            ... 11 more
  • Примечание Как видно, person.txt "неправильно" названо notperson.txt чтобы вызвать ошибку.

Обратите внимание, что на этом этапе база данных будет существовать (из-за openOrCreateDatabase) и будет содержать две таблицы (sqlite_master и android_metadata), но не таблицу person, например: -

enter image description here

Однако создание правильного файла активов person.txt (переименование notperson.txt в person.text) приведет ктаблица создается и загружается с данными: -

например, если person.txt: -

enter image description here

После запуска приложениярезультат в журнале, содержащем: -

06-10 04:39:04.277 3325-3325/? D/PERSON ROW COUNT: The number of rows in the person table is 11
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...