zlib
is a widely-used, lossless data compression library,
which is available for Android 1.5 system images or higher. This recipe discusses the basic usage of the zlib
functions.
Readers are expected to know how to create an Android NDK project. We can refer to the Writing a Hello NDK program recipe of Chapter 1, Hello NDK for detailed instructions.
The following steps describe how to create a simple Android application which demonstrates the usage of zlib
library:
ZlibDemo
. Set the package name as cookbook.chapter7.zlibdemo
. Refer to the Loading native libraries and registering native methods recipe of Chapter 2, Java Native Interface for more detailed instructions.MainActivity.java
in the cookbook.chapter7.zlibdemo
package. The MainActivity.java
file loads the ZlibDemo
native library, and calls the native methods.mylog.h
, ZlibDemo.cpp
, and GzFileDemo.cpp
files under the jni
folder. The mylog.h
header file contains the Android native logcat
utility functions, while ZlibDemo.cpp
and GzFileDemo.cpp
files contain code for compression and decompression. A part of the code in ZlibDemo.cpp
and GzFileDemo.cpp
is shown in the following code.ZlibDemo.cpp
contains the native code to compress and decompress data in memory.
compressUtil
compresses and decompress data in memory.
void compressUtil(unsigned long originalDataLen) { int rv; int compressBufBound = compressBound(originalDataLen); compressedBuf = (unsigned char*) malloc(sizeof(unsigned char)*compressBufBound); unsigned long compressedDataLen = compressBufBound; rv = compress2(compressedBuf, &compressedDataLen, dataBuf, originalDataLen, 6); if (Z_OK != rv) { LOGE(1, "compression error"); free(compressedBuf); return; } unsigned long decompressedDataLen = S_BUF_SIZE; rv = uncompress(decompressedBuf, &decompressedDataLen, compressedBuf, compressedDataLen); if (Z_OK != rv) { LOGE(1, "decompression error"); free(compressedBuf); return; } if (0 == memcmp(dataBuf, decompressedBuf, originalDataLen)) { LOGI(1, "decompressed data same as original data"); } //free resource free(compressedBuf); }
naCompressAndDecompress
generates data for compression and calls the compressUtil
function to compress and decompress the generated data:void naCompressAndDecompress(JNIEnv* pEnv, jclass clazz) { unsigned long originalDataLen = getOriginalDataLen(); LOGI(1, "---------data with repeated bytes---------") generateOriginalData(originalDataLen); compressUtil(originalDataLen); LOGI(1, "---------data with random bytes---------") generateOriginalDataRandom(originalDataLen); compressUtil(originalDataLen); }
GzFileDemo.cpp
contains the native code to compress and decompress the data in file.
writeToFile
writes a string to a gzip
file. Compression is applied at writing:
int writeToFile() { gzFile file; file = gzopen("/sdcard/test.gz", "w6"); if (NULL == file) { LOGE(1, "cannot open file to write"); return 0; } const char* dataStr = "hello, Android NDK!"; int bytesWritten = gzwrite(file, dataStr, strlen(dataStr)); gzclose(file); return bytesWritten; }
readFromFile
reads data from the gzip
file.
Decompression is applied at reading:
void readFromFile(int pBytesToRead) { gzFile file; file = gzopen("/sdcard/test.gz", "r6"); if (NULL == file) { LOGE(1, "cannot open file to read"); return; } char readStr[100]; int bytesRead = gzread(file, readStr, pBytesToRead); gzclose(file); LOGI(1, "%d: %s", bytesRead, readStr); }
Android.mk
file under the jni
folder with the following content:LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := ZlibDemo LOCAL_SRC_FILES := ZlibDemo.cpp GzFileDemo.cpp LOCAL_LDLIBS := -llog -lz include $(BUILD_SHARED_LIBRARY)
naCompressAndDecompress
function and disable the naGzFileDemo
function, build and run the application. We can monitor the logcat
output with the following command:$ adb logcat -v time ZlibDemo:I *:S
The logcat
output screenshot is shown as follows:
Enable the naGzFileDemo
function and disable the naCompressAndDecompress
function, build and run the application. The logcat
output is shown in the following screenshot:
The zlib
library provides compression and decompression functions for both in-memory data and files. We demonstrated both use cases. In the
ZlibDemo.cpp
file, we created two data buffers, one with repeated bytes, and the other with random bytes. We compress and decompress the data with the following steps:
uLong compressBound(uLong sourceLen);
The function returns the maximum size of the compressed data after calling the compress
or compress2
function on sourceLen
bytes of source data.
int compress2(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level);
This function accepts five input arguments. source
and sourceLen
refer to the source data buffer and source data length. dest
and destLen
indicate the data buffer for storing the compressed data and size of this buffer. The value of
destLen
must be at least the value returned by compressBound
when the function is called. When the function is returned, destLen
is set to the actual size of the compressed data. The last input argument level
can be a value between 0 and 9, where 1 gives best speed and 9 gives best compression. In our example, we set
the value as 6 to compromise between speed and compression.
uncompress
function:int uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
The input arguments have the same meaning as the compress2
function.
By default, these functions use the zlib
format for the compressed data.
This library also supports reading and writing files in the gzip
format. This is demonstrated in GzFileDemo.cpp
. The usage of these functions is similar to the
stdio
functions for file reading and writing.
The steps we followed to write compressed data to a gzip
file and then read the uncompressed data from it are shown as follows:
gzip
file for writing. This is done by the following function:gzFile gzopen(const char *path, const char *mode);
The function accepts a filename and open mode, and returns a gzFile
object on success. The
mode is similar to the fopen
function, but with an optional compression level. In our example, we called the gzopen
with w6
to specify the compression level as 6.
gzip
file. This is done by the following function:int gzwrite(gzFile file, voidpc buf, unsigned len);
This function writes uncompressed data into the compressed file. The input argument file
refers to the compressed file, buf
refers to the uncompressed data buffer, and len
indicates the number of bytes to write. The function returns the actual number of uncompressed data written.
gzip
file. This is done by the following function:int ZEXPORT gzclose(gzFile file);
Calling this function will flush all pending output and close the compressed file.
r6
to the gzopen
function.gzread
function.int gzread(gzFile file, voidp buf, unsigned len);
The function reads len
number of bytes from file into buf
. It returns the actual number of bytes read.
The zlib
library supports two compression formats, zlib
and gzip
. zlib
is designed to be compact and fast, so it is best for use in memory and on communication channels. On the other hand, gzip
is designed for single file compression on a filesystem, which has a larger header for maintaining the directory information, and uses a slower check method than zlib
.
In order to use the zlib
library, we must include the zlib.h
header file in our source code and add the following line to Android.mk
to link to the libz.so
library:
LOCAL_LDLIBS := -lz
Recall in the Managing assets at Android NDK recipe in Chapter 5, Android Native Application AP , we compiled the
libpng
library, which requires the zlib
library.
We only covered a few functions provided by the zlib
library.
For more information, you can refer to the zlib.h
and zconf.h
header files in the platforms/android-<version>/arch-arm/usr/include/
folder. Detailed documentation for the
zlib
library can be found at http://www.zlib.net/manual.html.