Отправьте приложение с базой данных - PullRequest
935 голосов
/ 04 февраля 2009

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

  1. Подготовьте базу данных SQLite и включите ее в .apk?

  2. Включить SQL-команды с приложением и заставить его создать базу данных и вставить данные при первом использовании?

Недостатки, которые я вижу:

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

  2. Создание и заполнение базы данных на устройстве может занять очень много времени.

Есть предложения? Указатели на документацию по любым вопросам будут с благодарностью.

Ответы [ 16 ]

4 голосов
/ 05 февраля 2009

В настоящее время нет способа создать базу данных SQLite для поставки с вашим apk. Лучшее, что вы можете сделать, это сохранить соответствующий SQL как ресурс и запустить его из вашего приложения. Да, это приводит к дублированию данных (такая же информация существует как ресурс и база данных), но другого пути сейчас нет. Единственным смягчающим фактором является сжатый файл apk. Мой опыт сжатия составляет 908 КБ до менее чем 268 КБ.

В приведенной ниже теме есть лучшее обсуждение / решение, которое я нашел с хорошим примером кода.

http://groups.google.com/group/android-developers/msg/9f455ae93a1cf152

Я сохранил свой оператор CREATE как строковый ресурс для чтения с помощью Context.getString () и запустил его с помощью SQLiteDatabse.execSQL ().

Я сохранил данные для своих вставок в res / raw / insertts.sql (я создал файл sql, более 7000 строк). Используя технику, приведенную выше, я зашел в цикл, построчно прочитал файл, соединил данные в «INSERT INTO tbl VALUE» и сделал еще один SQLiteDatabase.execSQL (). Нет смысла сохранять 7000 «INSERT INTO tbl VALUE», когда их можно только конкатенировать.

Это занимает около двадцати секунд на эмуляторе, я не знаю, сколько времени это займет на реальном телефоне, но это происходит только один раз, когда пользователь впервые запускает приложение.

3 голосов
/ 28 октября 2014

Android уже обеспечивает основанный на версии подход управления базой данных. Этот подход был использован в среде BARACUS для приложений Android.

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

Кроме того, он позволяет запускать горячее резервное копирование и горячее восстановление SQLite.

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

Поскольку это Apache License 2, вы можете свободно использовать любую часть кода, , которую можно найти на github

РЕДАКТИРОВАТЬ:

Если вы хотите только отправлять данные, вы можете рассмотреть возможность создания и сохранения POJO при первом запуске приложений. BARACUS получил встроенную поддержку для этого (встроенное хранилище значений ключей для информации о конфигурации, например, «APP_FIRST_RUN» плюс ловушка после контекста для начальной загрузки для запуска операций после запуска в контексте). Это позволяет вам получать тесно связанные данные с вашим приложением; в большинстве случаев это подходит для моих случаев использования.

3 голосов
/ 21 декабря 2009

Если требуемые данные не слишком велики (ограничения, которые я не знаю, будут зависеть от многих вещей), вы также можете загрузить данные (в формате XML, JSON и т. Д.) С веб-сайта / веб-приложения. После получения выполните операторы SQL, используя полученные данные, создавая таблицы и вставляя данные.

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

2 голосов
/ 12 июля 2017

Я использую ORMLite, и код ниже работал для меня

public class DatabaseProvider extends OrmLiteSqliteOpenHelper {
    private static final String DatabaseName = "DatabaseName";
    private static final int DatabaseVersion = 1;
    private final Context ProvidedContext;

    public DatabaseProvider(Context context) {
        super(context, DatabaseName, null, DatabaseVersion);
        this.ProvidedContext= context;
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        boolean databaseCopied = preferences.getBoolean("DatabaseCopied", false);
        if (databaseCopied) {
            //Do Nothing
        } else {
            CopyDatabase();
            SharedPreferences.Editor editor = preferences.edit();
            editor.putBoolean("DatabaseCopied", true);
            editor.commit();
        }
    }

    private String DatabasePath() {
        return "/data/data/" + ProvidedContext.getPackageName() + "/databases/";
    }

    private void CopyDatabase() {
        try {
            CopyDatabaseInternal();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private File ExtractAssetsZip(String zipFileName) {
        InputStream inputStream;
        ZipInputStream zipInputStream;
        File tempFolder;
        do {
            tempFolder = null;
            tempFolder = new File(ProvidedContext.getCacheDir() + "/extracted-" + System.currentTimeMillis() + "/");
        } while (tempFolder.exists());

        tempFolder.mkdirs();

        try {
            String filename;
            inputStream = ProvidedContext.getAssets().open(zipFileName);
            zipInputStream = new ZipInputStream(new BufferedInputStream(inputStream));
            ZipEntry zipEntry;
            byte[] buffer = new byte[1024];
            int count;

            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                filename = zipEntry.getName();
                if (zipEntry.isDirectory()) {
                    File fmd = new File(tempFolder.getAbsolutePath() + "/" + filename);
                    fmd.mkdirs();
                    continue;
                }

                FileOutputStream fileOutputStream = new FileOutputStream(tempFolder.getAbsolutePath() + "/" + filename);
                while ((count = zipInputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer, 0, count);
                }

                fileOutputStream.close();
                zipInputStream.closeEntry();
            }

            zipInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        return tempFolder;
    }

    private void CopyDatabaseInternal() throws IOException {

        File extractedPath = ExtractAssetsZip(DatabaseName + ".zip");
        String databaseFile = "";
        for (File innerFile : extractedPath.listFiles()) {
            databaseFile = innerFile.getAbsolutePath();
            break;
        }
        if (databaseFile == null || databaseFile.length() ==0 )
            throw new RuntimeException("databaseFile is empty");

        InputStream inputStream = new FileInputStream(databaseFile);

        String outFileName = DatabasePath() + DatabaseName;

        File destinationPath = new File(DatabasePath());
        if (!destinationPath.exists())
            destinationPath.mkdirs();

        File destinationFile = new File(outFileName);
        if (!destinationFile.exists())
            destinationFile.createNewFile();

        OutputStream myOutput = new FileOutputStream(outFileName);

        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }

        myOutput.flush();
        myOutput.close();
        inputStream.close();
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource, int fromVersion, int toVersion) {

    }
}

Обратите внимание, код извлекает файл базы данных из zip-файла в активах

2 голосов
/ 12 апреля 2017

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

public class DatabaseHelper extends SQLiteOpenHelper {
    private static String DB_NAME = "info.db";
    private static String DB_PATH = "";
    private static final int DB_VERSION = 1;

    private SQLiteDatabase mDataBase;
    private final Context mContext;
    private boolean mNeedUpdate = false;

    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        if (android.os.Build.VERSION.SDK_INT >= 17)
            DB_PATH = context.getApplicationInfo().dataDir + "/databases/";
        else
            DB_PATH = "/data/data/" + context.getPackageName() + "/databases/";
        this.mContext = context;

        copyDataBase();

        this.getReadableDatabase();
    }

    public void updateDataBase() throws IOException {
        if (mNeedUpdate) {
            File dbFile = new File(DB_PATH + DB_NAME);
            if (dbFile.exists())
                dbFile.delete();

            copyDataBase();

            mNeedUpdate = false;
        }
    }

    private boolean checkDataBase() {
        File dbFile = new File(DB_PATH + DB_NAME);
        return dbFile.exists();
    }

    private void copyDataBase() {
        if (!checkDataBase()) {
            this.getReadableDatabase();
            this.close();
            try {
                copyDBFile();
            } catch (IOException mIOException) {
                throw new Error("ErrorCopyingDataBase");
            }
        }
    }

    private void copyDBFile() throws IOException {
        InputStream mInput = mContext.getAssets().open(DB_NAME);
        //InputStream mInput = mContext.getResources().openRawResource(R.raw.info);
        OutputStream mOutput = new FileOutputStream(DB_PATH + DB_NAME);
        byte[] mBuffer = new byte[1024];
        int mLength;
        while ((mLength = mInput.read(mBuffer)) > 0)
            mOutput.write(mBuffer, 0, mLength);
        mOutput.flush();
        mOutput.close();
        mInput.close();
    }

    public boolean openDataBase() throws SQLException {
        mDataBase = SQLiteDatabase.openDatabase(DB_PATH + DB_NAME, null, SQLiteDatabase.CREATE_IF_NECESSARY);
        return mDataBase != null;
    }

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

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (newVersion > oldVersion)
            mNeedUpdate = true;
    }
}

Использование класса.

В классе действия объявите переменные.

private DatabaseHelper mDBHelper;
private SQLiteDatabase mDb;

В методе onCreate напишите следующий код.

mDBHelper = new DatabaseHelper(this);

try {
    mDBHelper.updateDataBase();
} catch (IOException mIOException) {
    throw new Error("UnableToUpdateDatabase");
}

try {
    mDb = mDBHelper.getWritableDatabase();
} catch (SQLException mSQLException) {
    throw mSQLException;
}

Если вы добавляете файл базы данных в папку res / raw, используйте следующую модификацию класса.

public class DatabaseHelper extends SQLiteOpenHelper {
    private static String DB_NAME = "info.db";
    private static String DB_PATH = "";
    private static final int DB_VERSION = 1;

    private SQLiteDatabase mDataBase;
    private final Context mContext;
    private boolean mNeedUpdate = false;

    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        if (android.os.Build.VERSION.SDK_INT >= 17)
            DB_PATH = context.getApplicationInfo().dataDir + "/databases/";
        else
            DB_PATH = "/data/data/" + context.getPackageName() + "/databases/";
        this.mContext = context;

        copyDataBase();

        this.getReadableDatabase();
    }

    public void updateDataBase() throws IOException {
        if (mNeedUpdate) {
            File dbFile = new File(DB_PATH + DB_NAME);
            if (dbFile.exists())
                dbFile.delete();

            copyDataBase();

            mNeedUpdate = false;
        }
    }

    private boolean checkDataBase() {
        File dbFile = new File(DB_PATH + DB_NAME);
        return dbFile.exists();
    }

    private void copyDataBase() {
        if (!checkDataBase()) {
            this.getReadableDatabase();
            this.close();
            try {
                copyDBFile();
            } catch (IOException mIOException) {
                throw new Error("ErrorCopyingDataBase");
            }
        }
    }

    private void copyDBFile() throws IOException {
        //InputStream mInput = mContext.getAssets().open(DB_NAME);
        InputStream mInput = mContext.getResources().openRawResource(R.raw.info);
        OutputStream mOutput = new FileOutputStream(DB_PATH + DB_NAME);
        byte[] mBuffer = new byte[1024];
        int mLength;
        while ((mLength = mInput.read(mBuffer)) > 0)
            mOutput.write(mBuffer, 0, mLength);
        mOutput.flush();
        mOutput.close();
        mInput.close();
    }

    public boolean openDataBase() throws SQLException {
        mDataBase = SQLiteDatabase.openDatabase(DB_PATH + DB_NAME, null, SQLiteDatabase.CREATE_IF_NECESSARY);
        return mDataBase != null;
    }

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

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (newVersion > oldVersion)
            mNeedUpdate = true;
    }
}

http://blog.harrix.org/article/6784

2 голосов
/ 10 июля 2016

Я написал библиотеку , чтобы упростить этот процесс.

dataBase = new DataBase.Builder(context, "myDb").
//        setAssetsPath(). // default "databases"
//        setDatabaseErrorHandler().
//        setCursorFactory().
//        setUpgradeCallback()
//        setVersion(). // default 1
build();

Это создаст базу данных из assets/databases/myDb.db файла. Кроме того, вы получите все эти функции:

  • Загрузить базу данных из файла
  • Синхронизированный доступ к базе данных
  • Использование sqlite-android по запросу, Android-дистрибутив последних версий SQLite.

Клонируй его из github .

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