📜 ⬆️ ⬇️

Work with Zip and 7z archives

In mobile development, there is a need to make an application for work without the Internet. For example, a dictionary or directory that will be used in harsh field conditions. Then, for the application to work, you need to once download the archive with the data and keep it at home. This can be done through a request to the network, but you can also stitch the archive with the data inside the application.

According to the requirements of Google Play, the apk- file of the application should be no more than 50 MB, you can also attach two .obb add - on files of 2 gigabytes. The mechanism is simple, but difficult to operate, so it is best to meet the 50 MB and rejoice . And this will help us as much as two archive formats Zip and 7z .

Let's take a look at their work on the example of a ready-made test application ZipExample .

For tests, the sqlite database test_data.db was created. It contains 2 android_metadata tables - by tradition and my_test_data with a million lines:
')


The file size is 198 MB.

Let's make two archives test_data.zip (10.1 MB) and test_data.7z (3.05 MB).

Obviously, sqlite database files are very well compressed. From experience, I can say that the simpler the base structure is, the better it is compressed. Both of these files are located in the assets folder and will be unarchived in the process.



The appearance of the program is a window with text and two buttons:



Here is the zip file unpacking method:
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); } 

The unpacking class here is ZipInputStream; it is included in the java.util.zip package, and that in turn is in the standard Android SDK and therefore works out of the box, i.e. Nothing to download separately.

Here is the 7z archive unpacking method:

 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); } 

And his assistant:

  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; } 

First we copy the archive file from asserts , and then unzip it using SevenZFile . It is in the org.apache.commons.compress.archivers.sevenz package ; and therefore, before using it, you need to set the dependency in build.gradle : compile 'org.apache.commons: commons-compress: 1.8' .
Android Stuodio itself downloads the libraries, and if they are outdated, it will tell you about the availability of the update.

Here is the screen of the running application:



The size of the debug version of the application is 6.8 MB .
But its size in the device after unpacking:



Attention the question is who is in the black box what's in the cache?

In conclusion, I want to say that unpacking archives takes a long time and therefore it cannot be done in the main ( UI ) stream. This will cause the interface to hang. To avoid this, you can use AsyncTask , and better background service because the user may not wait for the unpacking and exit, and you will get an error (although if you do not put crutches in the onPostExecute method).

I would be glad to constructive criticism in the comments.

Source: https://habr.com/ru/post/264701/


All Articles