В мобильной разработке бывает потребность сделать приложение для работы без интернета. Например, словарь или справочник, который будет использоваться в суровых полевых условиях. Тогда для работы приложения нужно единожды выкачать архив с данными и сохранить его у себя. Сделать это можно посредством запроса в сеть, но так же можно зашить архив с данными внутрь приложения.
Согласно требованиям Google Play, apk-файл приложения должен быть не более 50 МБ, так же можно прикрепить два файла дополнения .obb по 2 гигабайта. Механизм простой, но сложный при эксплуатации, поэтому лучше всего уложиться в 50 МБ и возрадоваться. И в этом нам помогут целых два архивных формата Zip и 7z.
Давайте рассмотрим их работу на примере уже готового тестового приложения ZipExample.
Для тестов была создана sqlite база данных test_data.db. Она содержит 2 таблицы android_metadata — по традиции и my_test_data с миллионом строчек:
![](https://habrastorage.org/files/124/c56/1eb/124c561eb0cc41a98ef02d4c08a7514f.PNG)
Размер полученного файла составляет 198 МБ.
Сделаем два архива test_data.zip (10.1 МБ) и test_data.7z (3.05 МБ).
Как очевидно, файлы БД sqlite очень хорошо сжимаются. По опыту могу сказать, что чем проще структура базы, тем лучше сжимается. Оба этих файла располагаются в папке assets и в процессе работы будут разархивированы.
![](https://habrastorage.org/files/710/cbc/30e/710cbc30efca41deaa3c3a28312a12d8.PNG)
Внешний вид программы представляет собой окно с текстом и двумя кнопками:
![](https://habrastorage.org/r/w1560/files/689/928/0de/6899280de0be48379225bdd17a85846a.png)
Вот метод распаковки zip архива:
Распаковывающим классом тут является ZipInputStream он входит в пакет java.util.zip, а тот в свою очередь в стандартную Android SDK и поэтому работает «из коробки» т.е. ничего отдельно закачивать не надо.
Вот метод распаковки 7z архива:
И его помощник:
Сначала мы копируем файл архива из asserts , а потом разархивируем при помощи SevenZFile . Он находится в пакете org.apache.commons.compress.archivers.sevenz; и поэтому перед его использованием нужно прописать в build.gradle зависимость: compile 'org.apache.commons:commons-compress:1.8'.
Android Stuodio сама скачает библиотеки, а если они устарели, то подскажет о наличии обновления.
Вот экран работающего приложения:
![](https://habrastorage.org/r/w1560/files/1c7/967/142/1c7967142e25432094a27ecbaebfa1a3.png)
Размер отладочной версии приложения получился 6,8 МБ.
А вот его размер в устройстве после распаковки:
![](https://habrastorage.org/r/w1560/files/8bd/dcc/a91/8bddcca915784d6bb8e47270d948bd07.png)
Внимание вопроскто в черном ящике что в кеше?
В заключении хочу сказать, что распаковка архивов занимает продолжительное время и поэтому нельзя его делать в основном (UI) потоке. Это приведет к подвисанию интерфейса. Во избежание этого можно задействовать AsyncTask , а лучше фоновый сервис т.к. пользователь может не дождаться распаковки и выйти, а вы получите ошибку (правда если не поставите костылей в методе onPostExecute).
Буду рад конструктивной критике в комментариях.
Согласно требованиям 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 и в процессе работы будут разархивированы.
Внешний вид программы представляет собой окно с текстом и двумя кнопками:
![](https://habrastorage.org/files/689/928/0de/6899280de0be48379225bdd17a85846a.png)
Вот метод распаковки 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 сама скачает библиотеки, а если они устарели, то подскажет о наличии обновления.
Вот экран работающего приложения:
![](https://habrastorage.org/files/1c7/967/142/1c7967142e25432094a27ecbaebfa1a3.png)
Размер отладочной версии приложения получился 6,8 МБ.
А вот его размер в устройстве после распаковки:
![](https://habrastorage.org/files/8bd/dcc/a91/8bddcca915784d6bb8e47270d948bd07.png)
Внимание вопрос
В заключении хочу сказать, что распаковка архивов занимает продолжительное время и поэтому нельзя его делать в основном (UI) потоке. Это приведет к подвисанию интерфейса. Во избежание этого можно задействовать AsyncTask , а лучше фоновый сервис т.к. пользователь может не дождаться распаковки и выйти, а вы получите ошибку (правда если не поставите костылей в методе onPostExecute).
Буду рад конструктивной критике в комментариях.