Intereting Posts
Как разрешить эту ошибку VFY: невозможно разрешить виртуальный метод ListView в ArrayAdapter order get смешивается при прокрутке Как вы скомпилируете проект Android в файл .apk в Eclipse без запуска эмулятора? Как разрешить Session 'app': ошибка? Установите версию приложения для Android с помощью Gradle Настройка фильтра на основе пользовательских данных Почему XML используется для создания макетов пользовательского интерфейса в Android? Android Intent для запуска приложения Google+ на экране Google+ Community Как использовать getSystemService в классе без активности? Как показать описание подсказки в Android Studio? Эмулятор Android не запускается, avd Как найти учетную запись Gmail, связанную с Android Market? Получить информацию о значении браузера android и отобразить его на экране Различия между / sdcard / emulated / 0 и / sdcard Как установить приложение в систему / приложение при разработке из студии Android

Самый быстрый и эффективный способ предварительного заполнения базы данных в Android

Если вы хотите предварительно заполнить базу данных (SQLite) в Android, это не так просто, как можно было бы подумать.

Поэтому я нашел этот урок, который часто упоминается здесь и в Stack Overflow.

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

Итак, я думал, что можно создать базу данных в обработчике onCreate () как обычно, а затем загрузить файл (.sql) из / assets, который содержит инструкции для заполнения значений:

INSERT INTO testTable (name, pet) VALUES ('Mike', 'Tiger'); INSERT INTO testTable (name, pet) VALUES ('Tom', 'Cat'); ... 

Но вызов execSQL () в обработчике onCreate () на самом деле не работает. Кажется, что файл / assets не должен иметь более 1 МБ, а execSQL () выполняет только первый оператор (Mike – Tiger).

Что бы вы сделали, предварительно заполнив базу данных?

Solutions Collecting From Web of "Самый быстрый и эффективный способ предварительного заполнения базы данных в Android"

Я предлагаю следующее:

  1. Заверните всю вашу логику INSERT в транзакцию ( BEGIN... COMMIT или через API beginTransaction () … endTransaction () )
  2. Как уже было предложено, используйте API-интерфейсы связывания и объекты утилизации .
  3. Не создавайте индексы до тех пор, пока эта объемная вставка не будет завершена.

Кроме того, взгляните на более быстрые объемные вставки в sqlite3?

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

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

В моем приложении мне нужно иметь около 2600 строк (с 4 столбцами) в БД при первом запуске – это данные для автозаполнения и несколько других вещей. Он будет изменен довольно редко (пользователи могут добавлять пользовательские записи, но большую часть времени им это не нужно) и довольно большой. Заполнение его из операторов SQL занимает не только значительно больше времени, но и больше места в APK (предполагая, что я буду хранить данные внутри него, иначе я мог бы загрузить его из Интернета).

Это очень простой случай («Большая» вставка может иметь место только один раз и только при первом запуске), и я решил пойти с копированием предварительно заполненного файла DB. Конечно, это не самый приятный способ, но это быстрее. Я хочу, чтобы мои пользователи могли использовать приложение настолько быстро, насколько это возможно, и относиться к скорости как к приоритету – и им это действительно нравится. Напротив, я сомневаюсь, что они будут рады, когда приложение будет замедляться, потому что я думал, что более медленное и приятное решение на самом деле лучше.

Если вместо 2600 моя таблица имела бы первоначально ~ 50 строк, я бы пошел с операторами SQL, так как разница в скорости и размере не была бы такой большой.

Вы должны решить, какое решение лучше подходит вашему делу. Если вы предвидите любые проблемы, которые могут возникнуть в результате использования опции «prepopulated db» – не используйте ее. Если вы не уверены в этих проблемах, спросите, предоставив более подробную информацию о том, как вы будете использовать (и в конечном итоге, обновлять) содержимое БД. Если вы не совсем уверены, какое решение будет быстрее, сравните его. И не бойтесь этого метода копирования файлов – он может работать очень хорошо, если использовать его разумно.

Вы можете получить свой торт и съесть его тоже. Вот решение, которое может как уважать использование вашего адаптера db, так и использовать простой (и гораздо более быстрый) процесс копирования для предварительно заполненной базы данных.

Я использую адаптер db на основе одного из примеров Google. Он включает внутренний класс dbHelper (), который расширяет класс SQLiteOpenHelper () Android. Хитрость заключается в том, чтобы переопределить метод onCreate (). Этот метод вызывается только тогда, когда помощник не может найти базу данных, на которую вы ссылаетесь, и ей необходимо создать БД для вас. Это должно произойти только в первый раз, когда он вызывается при любой установке устройства, и это единственный раз, когда вы хотите скопировать БД. Поэтому переопределите это так:

  @Override public void onCreate(SQLiteDatabase db) { mNeedToCopyDb = true; } 

Конечно, убедитесь, что вы впервые объявили и инициализировали этот флаг в DbHelper –

  private Boolean mNeedToCopyDb = false; 

Теперь, в вашем открытом () методе dbAdapter, вы можете проверить, нужно ли вам копировать БД. Если вы это сделаете, закройте помощника, скопируйте БД и затем, наконец, откройте новый помощник (см. Ниже код). Все будущие попытки открыть db с помощью адаптера db найдут вашу (скопированную) БД, и поэтому метод onCreate () внутреннего класса DbHelper не будет вызываться, а флаг mNeedToCopyDb останется ложным.

  /** * Open the database using the adapter. If it cannot be opened, try to * create a new instance of the database. If it cannot be created, * throw an exception to signal the failure. * * @return this (self reference, allowing this to be chained in an * initialization call) * @throws SQLException if the database could neither be opened nor created */ public MyDbAdapter open() throws SQLException { mDbHelper = new DatabaseHelper(mCtx); mDb = mDbHelper.getReadableDatabase(); if (mDbHelper.mNeedToCopyDb == true){ mDbHelper.close(); try { copyDatabase(); } catch (IOException e) { e.printStackTrace(); } finally { mDbHelper = new DatabaseHelper(mCtx); mDb = mDbHelper.getReadableDatabase(); } } return this; } 

Просто поместите код, чтобы сделать копию базы данных внутри вашего адаптера db в методе copyDatabase (), как описано выше. Вы можете использовать значение mDb, которое было обновлено первым экземпляром DbHelper (когда он создавал БД-заглушку), чтобы получить путь к использованию для вашего выходного потока при копировании. Постройте свой входной поток следующим образом:

 dbInputStream = mCtx.getResources().openRawResource(R.raw.mydatabase); 

[Примечание: если ваш файл DB слишком велик, чтобы копировать за один глоток, просто разделите его на несколько частей.]

Это работает очень быстро и помещает весь код доступа db (включая копирование БД, если необходимо) в ваш адаптер db.

Я написал класс DbUtils, похожий на предыдущий ответ. Он является частью инструмента ORM greenDAO и доступен на github . Разница в том, что он попытается найти границы операторов, используя простое регулярное выражение, а не только окончание строк. Если вам приходится полагаться на файл SQL, я сомневаюсь, что существует более быстрый способ.

Но, если вы можете предоставить данные в другом формате, это должно быть значительно быстрее, чем использование SQL-скрипта. Хитрость заключается в использовании скомпилированного заявления . Для каждой строки данных вы связываете анализируемые значения с оператором и выполняете оператор. И, конечно же, вам нужно сделать это внутри транзакции. Я бы порекомендовал простой формат разделенного разделителя (например, CSV), потому что он может анализироваться быстрее, чем XML или JSON.

Мы провели некоторые тесты производительности для greenDAO. Для наших тестовых данных мы имели скорость вставки около 5000 рядов в секунду. По какой-то причине ставка снизилась до половины с Android 4.0 .

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

И exesql поддерживают более sql-предложение, вот вам пример:

  BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(asManager.open(INIT_FILE)), 1024 * 4); String line = null; db.beginTransaction(); while ((line = br.readLine()) != null) { db.execSQL(line); } db.setTransactionSuccessful(); } catch (IOException e) { FLog.e(LOG_TAG, "read database init file error"); } finally { db.endTransaction(); if (br != null) { try { br.close(); } catch (IOException e) { FLog.e(LOG_TAG, "buffer reader close error"); } } } 

Выше пример требует, чтобы INIT_FILE нуждалась в каждой строке, это предложение sql .

Кроме того, если ваш файл предложений sql большой, вы можете создать базу данных вне сайта android (поддержка sqlite для окон, linux, чтобы вы могли создать базу данных в своих os и скопировать файл базы данных в свою папку с ресурсами, Вы можете закрепить его)

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

Надежда может вам помочь -):

Я использовал этот метод. Сначала создайте свою базу данных sqlite, есть несколько программ, которые вы можете использовать. Мне нравится SqliteBrowser . Затем скопируйте файл базы данных в свою папку с ресурсами. Затем вы можете использовать этот код в конструкторе SQLiteOpenHelper.

 final String outFileName = DB_PATH + NAME; if(! new File(outFileName).exists()){ this.getWritableDatabase().close(); //Open your local db as the input stream final InputStream myInput = ctx.getAssets().open(NAME, Context.MODE_PRIVATE); //Open the empty db as the output stream final OutputStream myOutput = new FileOutputStream(outFileName); //final FileOutputStream myOutput = context.openFileOutput(outFileName, Context.MODE_PRIVATE); //transfer bytes from the inputfile to the outputfile final byte[] buffer = new byte[1024]; int length; while ((length = myInput.read(buffer))>0){ myOutput.write(buffer, 0, length); } //Close the streams myOutput.flush(); ((FileOutputStream) myOutput).getFD().sync(); myOutput.close(); myInput.close(); } } catch (final Exception e) { // TODO: handle exception } 

DB_PATH – это что-то вроде /data/data/com.mypackage.myapp/databases/

NAME – это любое имя базы данных, которое вы выберете «mydatabase.db»

Я знаю, что в этом коде есть много улучшений, но он работает так хорошо и ОЧЕНЬ БЫСТРО. Поэтому я оставил его в покое. Подобным образом это может быть еще лучше в методе onCreate (). Также проверять, существует ли файл каждый раз, вероятно, не самый лучший. В любом случае, как я сказал, это работает, это быстро и надежно.

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