Вопрос по android, sqlite, download – Загрузите базу данных SQLite из Интернета и загрузите в приложение Android

6

Для моего приложения для Android я бы хотел использовать большую базу данных (около 45 МБ).

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

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

Поэтому я бы предпочел загрузить базу данных из Интернета (веб-сервер) при первом запуске. Как я мог это сделать? Могу ли я загрузить полный файл SQLite и сохранить его в каталоге базы данных? Или мне лучше использовать файлы данных JSON, которые используются для заполнения базы данных?

Ваш Ответ

3   ответа
3

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

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

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

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

Спасибо вам! Конечно, важен аспект обновлений, который не должен каждый раз включать большой файл базы данных. Поэтому я обязательно должен пойти с небольшим приложением, которое загружает все необходимые данные при первом запуске. Но, как предполагает Commonsware, JSON кажется медленнее, чем просто загрузка полного файла базы данных SQLite. caw
11

folder and copy it to the database directory on the first startup.

Это не должно быть разделено, только ZIPped. УвидетьSQLiteAssetHelper для примера.

How could I do this?

использованиеHttpUrlConnection, Или используйтеHttpClient.

Can I download a complete SQLite file and save it to the database directory?

Да. использованиеgetDatabasePath() чтобы получить правильный локальный путь для использования.

Or should I rather go with JSON data files that are used to populate the database?

Вы могли бы, но для 45 МБ, это было бы ужасно медленно.

@ MarcoW .: "Максимальный размер активов составляет 1 МБ, не так ли?" - AFAIK, это максимальный размер актива, который сжимаетсяaapt, Вы можете иметь большие активы, что, еслиaapt не сжать их, вот почемуSQLiteAssetHelper использует ZIP-файл. Ваши аргументы для загрузки с веб-сервера по-прежнему очень верны (например, повторяется место) - я просто уточнил ваш разделенный раздел. ссылка, для всех, кто сталкивался с этим вопросом. Кстати, вы также можете использоватьDownloadManager загрузить базу данных, хотя после загрузки вы бы переместили ее в конечное положение самостоятельно.
Спасибо!SQLiteAssetHelper выглядит интересно, но, тем не менее, максимальный размер активов составляет 1 МБ, не так ли? Таким образом, не имеет значения, является ли файл заархивированным или нет - это ограничение. Поэтому вы бы порекомендовали использоватьSQLiteAssetHelper или загрузка с веб-сервера? caw
7

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

MyActivity.java

public class MyActivity extends Activity {

    private static final String SD_CARD_FOLDER = "MyApp";
    private static final String DB_DOWNLOAD_PATH = "http://www.example.org/downloads/dictionary.sqlite";
    private Database mDB = null;
    private DatabaseDownloadTask mDatabaseDownloadTask = null;
    private DatabaseOpenTask mDatabaseOpenTask = null;

    private class DatabaseDownloadTask extends AsyncTask<Context, Integer, Boolean> {

        @Override
        protected void onPreExecute() {
            mProgressDialog = new ProgressDialog(MyActivity.this);
            mProgressDialog.setTitle(getString(R.string.please_wait));
            mProgressDialog.setMessage(getString(R.string.downloading_database));
            mProgressDialog.setIndeterminate(false);
            mProgressDialog.setMax(100);
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            mProgressDialog.setCancelable(false);
            mProgressDialog.show();
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }

        @Override
        protected Boolean doInBackground(Context... params) {
            try {
                File dbDownloadPath = new File(Database.getDatabaseFolder());
                if (!dbDownloadPath.exists()) {
                    dbDownloadPath.mkdirs();
                }
                HttpParams httpParameters = new BasicHttpParams();
                HttpConnectionParams.setConnectionTimeout(httpParameters, 5000);
                HttpConnectionParams.setSoTimeout(httpParameters, 5000);
                DefaultHttpClient client = new DefaultHttpClient(httpParameters);
                HttpGet httpGet = new HttpGet(DB_DOWNLOAD_PATH);
                InputStream content = null;
                try {
                    HttpResponse execute = client.execute(httpGet);
                    if (execute.getStatusLine().getStatusCode() != 200) { return null; }
                    content = execute.getEntity().getContent();
                    long downloadSize = execute.getEntity().getContentLength();
                    FileOutputStream fos = new FileOutputStream(Database.getDatabaseFolder()+Database.DATABASE_NAME+".sqlite");
                    byte[] buffer = new byte[256];
                    int read;
                    long downloadedAlready = 0;
                    while ((read = content.read(buffer)) != -1) {
                        fos.write(buffer, 0, read);
                        downloadedAlready += read;
                        publishProgress((int) (downloadedAlready*100/downloadSize));
                    }
                    fos.flush();
                    fos.close();
                    content.close();
                    return true;
                }
                catch (Exception e) {
                    if (content != null) {
                        try {
                            content.close();
                        }
                        catch (IOException e1) {}
                    }
                    return false;
                }
            }
            catch (Exception e) {
                return false;
            }
        }

        protected void onProgressUpdate(Integer... values) {
            if (mProgressDialog != null) {
                if (mProgressDialog.isShowing()) {
                    mProgressDialog.setProgress(values[0]);
                }
            }
        }

        @Override
        protected void onPostExecute(Boolean result) {
            if (mProgressDialog != null) {
                mProgressDialog.dismiss();
                mProgressDialog = null;
            }
            if (result.equals(Boolean.TRUE)) {
                Toast.makeText(MyActivity.this, getString(R.string.database_download_success), Toast.LENGTH_LONG).show();
                mDatabaseOpenTask = new DatabaseOpenTask();
                mDatabaseOpenTask.execute(new Context[] { MyActivity.this });
            }
            else {
                Toast.makeText(getApplicationContext(), getString(R.string.database_download_fail), Toast.LENGTH_LONG).show();
                finish();
            }
        }

    }

    private class DatabaseOpenTask extends AsyncTask<Context, Void, Database> {

        @Override
        protected Database doInBackground(Context ... ctx) {
            try {
                String externalBaseDir = Environment.getExternalStorageDirectory().getAbsolutePath();
                // DELETE OLD DATABASE ANFANG
                File oldFolder = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+SD_CARD_FOLDER);
                File oldFile = new File(oldFolder, "dictionary.sqlite");
                if (oldFile.exists()) {
                    oldFile.delete();
                }
                if (oldFolder.exists()) {
                    oldFolder.delete();
                }
                // DELETE OLD DATABASE ENDE
                File newDB = new File(Database.getDatabaseFolder()+"dictionary.sqlite");
                if (newDB.exists()) {
                    return new Database(ctx[0]);
                }
                else {
                    return null;
                }
            }
            catch (Exception e) {
                return null;
            }
        }

        @Override
        protected void onPreExecute() {
            mProgressDialog = ProgressDialog.show(MainActivity.this, getString(R.string.please_wait), "Loading the database! This may take some time ...", true);
        }

        @Override
        protected void onPostExecute(Database newDB) {
            if (mProgressDialog != null) {
                mProgressDialog.dismiss();
                mProgressDialog = null;
            }
            if (newDB == null) {
                mDB = null;
                AlertDialog.Builder downloadDatabase = new AlertDialog.Builder(MyActivity.this);
                downloadDatabase.setTitle(getString(R.string.downloadDatabase));
                downloadDatabase.setCancelable(false);
                downloadDatabase.setMessage(getString(R.string.wantToDownloadDatabaseNow));
                downloadDatabase.setPositiveButton(getString(R.string.download), new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        mDatabaseDownloadTask = new DatabaseDownloadTask();
                        mDatabaseDownloadTask.execute();
                    }
                });
                downloadDatabase.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        finish();
                    }
                });
                downloadDatabase.show();
            }
            else {
                mDB = newDB;
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mDatabaseDownloadTask != null) {
            if (mDatabaseDownloadTask.getStatus() != AsyncTask.Status.FINISHED) {
                mDatabaseDownloadTask.cancel(true);
            }
        }
        if (mDatabaseOpenTask != null) {
            if (mDatabaseOpenTask.getStatus() != AsyncTask.Status.FINISHED) {
                mDatabaseOpenTask.cancel(true);
            }
        }
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
            mProgressDialog = null;
        }
        if (mDB != null) {
            mDB.close();
            mDB = null;
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            Toast.makeText(getApplicationContext(), getString(R.string.sd_card_not_found), Toast.LENGTH_LONG).show();
            finish();
        }
        mDatabaseOpenTask = new DatabaseOpenTask();
        mDatabaseOpenTask.execute(new Context[] { this });
    }

}

Database.java

открытый класс База данных расширяет SQLiteOpenHelper {

private static final String DATABASE_NAME = "dictionary";
private String DATABASE_PATH = null;
private static final int DATABASE_VERSION = 1;
private static final String PACKAGE_NAME = "com.my.package";
private SQLiteDatabase db;

public Database(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
    DATABASE_PATH = getDatabaseFolder()+DATABASE_NAME+".sqlite";
    db = getWritableDatabase();
}

public static String getDatabaseFolder() {
    return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Android/data/"+PACKAGE_NAME+"/databases/";
}

@Override
public synchronized SQLiteDatabase getWritableDatabase() {
    try {
        if (db != null) {
            if (db.isOpen()) {
                return db;
            }
        }
        return SQLiteDatabase.openDatabase(DATABASE_PATH, null, SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
    }
    catch (Exception e) {
        return null;
    }
}

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

@Override
public void onCreate(SQLiteDatabase db) { }

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

}

@LomaRoma Вы правы, теперь это лишнее. Однако, если вы не переименовываете файл, вы должны изменить имя при записи файла базы данных на диск. Я отредактировал ответ. Обратите внимание, что в этом фрагменте есть несколько других частей, которые могут быть улучшены или даже излишни. caw
У меня проблемы с загрузкой базы данных sqlite и ее использованием в приложении. По сути, SQLite выдает ошибку, что база данных повреждена, что не так. Когда я думаю об этом, создается впечатление, что ошибка повреждения возникает, когда БД изменяется без открытия (например, удалить старый файл БД + загрузить новый). Может ли это быть? Были ли у вас эти проблемы?
Есть особая причина для переименования БД? Файл tempFile = новый файл (Database.getDatabaseFolder () + & quot; temp.sqlite & quot;); tempFile.renameTo (новый файл (Database.getDatabaseFolder () + & quot; dictionary.sqlite & quot;));

Похожие вопросы