В мобильной разработке бывает потребность сделать приложение для работы без интернета.
Например словарь или справочник, который будет использоваться в суровых полевых условиях.
Тогда для работы приложения нужно единожды выкачать архив с данными и сохранить его у себя.
Сделать это можно посредством запроса в сеть, но так же можно зашить архив с данными внутрь приложения.
Согласно требованиям Google Play apk файл приложения должен быть не более 50 мб, так же можно прикрепить два файла дополнения .obb по 2 гигабайта. Механизм простой но сложный при эксплуатации, поэтому лучше всего уложиться в 50 мб и возрадоваться.
И в этом нам помогут целых два архивных формата Zip и 7z.
Давайте рассмотрим их работу на примере уже готового тестового приложения ZipExample.
Для тестов была создана sqlite база данных test_data.db.
Она содержит 2 таблицы android_metadata — по традиции и my_test_data с миллионом строчек
Размер полученного файла составляет 198 мб.
Сделаем два архива test_data.zip (10.1 мб) и test_data.7z (3.05).
Как очевидно файлы БД sqlite очень хорошо сжимаются. По опыту могу сказать, что чем проще структура базы, тем лучше сжимается.
Оба этих файла располагаются в папке assets и в процессе работы будут разархивированы.
Внешний вид программы представляет собой окно с текстом и двумя кнопками
Вот метод распаковки zip архива:
public void onUnzipZip(View v) throws IOException { SimpleDateFormat sdf = new SimpleDateFormat(" HH:mm:ss.SSS"); String currentDateandTime = sdf.format(new Date()); String log = mTVLog.getText().toString() + "\nStart unzip zip" + currentDateandTime; mTVLog.setText(log); InputStream is = getAssets().open("test_data.zip"); File db_path = getDatabasePath("zip.db"); if (!db_path.exists()) db_path.getParentFile().mkdirs(); OutputStream os = new FileOutputStream(db_path); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is)); ZipEntry ze; while ((ze = zis.getNextEntry()) != null) { byte[] buffer = new byte[1024]; int count; while ((count = zis.read(buffer)) > -1) { os.write(buffer, 0, count); } os.close(); zis.closeEntry(); } zis.close(); is.close(); currentDateandTime = sdf.format(new Date()); log = mTVLog.getText().toString() + "\nEnd unzip zip" + currentDateandTime; mTVLog.setText(log); }
Распаковывающим классом тут является ZipInputStream он входит в пакет java.util.zip, а тот в свою очередь в стандартную Android SDK и поэтому работает «из коробки» т.е. ничего отдельно закачивать не надо.
Вот метод распаковки 7z архива:
public void onUnzip7Zip(View v) throws IOException { SimpleDateFormat sdf = new SimpleDateFormat(" HH:mm:ss.SSS"); String currentDateandTime = sdf.format(new Date()); String log = mTVLog.getText().toString() + "\nStart unzip 7zip" + currentDateandTime; mTVLog.setText(log); File db_path = getDatabasePath("7zip.db"); if (!db_path.exists()) db_path.getParentFile().mkdirs(); SevenZFile sevenZFile = new SevenZFile(getAssetFile(this, "test_data.7z", "tmp")); SevenZArchiveEntry entry = sevenZFile.getNextEntry(); OutputStream os = new FileOutputStream(db_path); while (entry != null) { byte[] buffer = new byte[8192];// int count; while ((count = sevenZFile.read(buffer, 0, buffer.length)) > -1) { os.write(buffer, 0, count); } entry = sevenZFile.getNextEntry(); } sevenZFile.close(); os.close(); currentDateandTime = sdf.format(new Date()); log = mTVLog.getText().toString() + "\nEnd unzip 7zip" + currentDateandTime; mTVLog.setText(log); }
И его помощник:
public static File getAssetFile(Context context, String asset_name, String name) throws IOException { File cacheFile = new File(context.getCacheDir(), name); try { InputStream inputStream = context.getAssets().open(asset_name); try { FileOutputStream outputStream = new FileOutputStream(cacheFile); try { byte[] buf = new byte[1024]; int len; while ((len = inputStream.read(buf)) > 0) { outputStream.write(buf, 0, len); } } finally { outputStream.close(); } } finally { inputStream.close(); } } catch (IOException e) { throw new IOException("Could not open file" + asset_name, e); } return cacheFile; }
Сначала мы копируем файл архива из asserts , а потом разархивируем при помощи SevenZFile . Он находится в пакете org.apache.commons.compress.archivers.sevenz; и поэтому перед его использованием нужно прописать в build.gradle зависимость: compile ‘org.apache.commons:commons-compress:1.8’.
Android Stuodio сама скачает библиотеки, а если они устарели, то подскажет о наличии обновления.
Вот экран работающего приложения:
Размер отладочной версии приложения получился 6,8 МБ.
А вот его размер в устройстве после распаковки:
Внимание вопрос кто в черном ящике что в кеше?
В заключении хочу сказать, что распаковка архивов занимает продолжительное время и поэтому нельзя его делать в основном (UI) потоке. Это приведет к подвисанию интерфейса. Во избежание этого можно задействовать AsyncTask , а лучше фоновый сервис т.к. пользователь может не дождаться распаковки и выйти, а вы получите ошибку (правда если не поставите костылей в методе onPostExecute)
Буду рад конструктивной критике в комментариях.
ссылка на оригинал статьи http://habrahabr.ru/post/264701/
Добавить комментарий