Mobile Development 18 min read

Incremental Update Solution for Android APKs Using Binary Diff and Channel Management

The article presents an Android incremental‑update solution that uses bsdiff‑generated binary patches to replace full APK downloads, embeds channel identifiers in the ZIP comment field to avoid per‑channel APK duplication, and details server‑side patch creation, client‑side channel stripping, patch merging, and verification to reduce bandwidth and simplify multi‑channel distribution.

Tencent Music Tech Team
Tencent Music Tech Team
Tencent Music Tech Team
Incremental Update Solution for Android APKs Using Binary Diff and Channel Management

This article introduces an incremental upgrade scheme for Android applications. Users can obtain the new version by downloading only the added parts instead of the full installation package, which saves user and server bandwidth and solves multi‑channel distribution problems.

Background

As the "全民K歌" app evolves, its APK size keeps growing. Each full update forces users to download the entire new package. By using incremental updates, only the binary differences between the old and new versions are downloaded, greatly reducing traffic. For example, version 3.2 (30.4 MB) and version 3.3 (27.6 MB) differ by a 7.3 MB patch file (3.2_3.3.patch). Downloading the patch instead of the full 3.3 APK saves 20.3 MB.

The process described combines the locally installed karaoke_3.2.apk with the patch 3.2_3.3.patch to generate the new karaoke_3.3.apk .

Implementation Principle

1. Server Side

2. Client Side

The incremental update works by binary‑comparing the old and new APKs to produce a patch. When a user upgrades, the client downloads the appropriate patch from the server, then merges the local APK with the patch to create the new APK. The server must generate the patch in advance for each version pair.

Implementation Steps

1. Generate Patch

The binary diff tool bsdiff is used. After compiling the source (bsdiff.c, bspatch.c, makefile) on Linux, the command:

./bsdiff karaoke_3.2.apk karaoke_3.3.apk 3.2_3.3.patch

produces the 7.3 MB patch.

2. Solve Multi‑Channel Issue

(1) Multi‑Channel Explanation

Different markets (e.g., Tencent MyApp, Google Play) require channel identifiers embedded in the APK. Traditionally each channel has its own APK, leading to many separate patches.

(2) Write Channel ID into APK Comment Field

APK files are ZIP archives. The ZIP “Comment” field (last two bytes indicate length) can store a channel string without affecting installation. The following Java code writes the channel ID:

static final int SHORT_LENGTH = 2;
static final String UTF_8 = "UTF-8";
static final byte[] MAGIC = new byte[]{0x21, 0x5a, 0x58, 0x4b, 0x21}; // !ZXK!
public static void writeQUA(File file, String comment) throws IOException {
    byte[] data = comment.getBytes(UTF_8);
    RandomAccessFile raf = new RandomAccessFile(file, "rw");
    raf.seek(file.length() - SHORT_LENGTH);
    writeShort(data.length + SHORT_LENGTH + MAGIC.length, raf);
    writeBytes(data, raf);
    writeShort(data.length, raf);
    writeBytes(MAGIC, raf);
    raf.close();
}
... // helper methods omitted for brevity

(3) Read Channel ID

public static String readQUA(File file) throws IOException {
    RandomAccessFile raf = new RandomAccessFile(file, "r");
    long index = raf.length();
    byte[] buffer = new byte[MAGIC.length];
    index -= MAGIC.length;
    raf.seek(index);
    raf.readFully(buffer);
    if (isMagicMatched(buffer)) {
        index -= SHORT_LENGTH;
        raf.seek(index);
        int length = readShort(raf);
        if (length > 0) {
            index -= length;
            raf.seek(index);
            byte[] bytesComment = new byte[length];
            raf.readFully(bytesComment);
            return new String(bytesComment, UTF_8);
        }
    }
    return null;
}

3. Assemble New APK

(1) Remove Existing Channel ID

public static int deleteQua(File src, File dest) throws IOException {
    // ... implementation that truncates the comment field ...
}

(2) Merge Patch with Clean APK

After obtaining a channel‑free local APK and the downloaded patch, the client verifies file integrity (e.g., MD5) before invoking bspatch to produce the new APK. The native C implementation of bspatch is integrated into a shared library and called from Java via JNI:

#include
#include
#include
#include
#include
#include
#include
#include
... // full C source omitted for brevity
JNIEXPORT jint Java_com_tencent_smartpatch_utils_PatchUtils_patch(JNIEnv *env, jclass obj, jstring old_apk, jstring new_apk, jstring patch) {
    char *ch[4];
    ch[0] = "bspatch";
    ch[1] = (char*)(*env)->GetStringUTFChars(env, old_apk, 0);
    ch[2] = (char*)(*env)->GetStringUTFChars(env, new_apk, 0);
    ch[3] = (char*)(*env)->GetStringUTFChars(env, patch, 0);
    int ret = applypatch(4, ch);
    (*env)->ReleaseStringUTFChars(env, old_apk, ch[1]);
    (*env)->ReleaseStringUTFChars(env, new_apk, ch[2]);
    (*env)->ReleaseStringUTFChars(env, patch, ch[3]);
    return ret;
}

Calling PatchUtils.patch(oldApkPath, newApkPath, patchPath) finally generates the updated APK.

Conclusion

The complete workflow is:

1. Build an APK without a channel ID. 2. Server generates a binary diff between the old and new channel‑free APKs. 3. Server writes the appropriate channel ID into the new APK for distribution. 4. Client removes the channel ID from its installed APK. 5. Client downloads the matching patch. 6. Client merges the clean APK with the patch to obtain the latest version.

References

1. bsdiff project 2. ZIP file format 3. SmartAppUpdates 4. packer-ng-plugin

mobile developmentAndroidAPK Patchbinary diffbsdiffChannel ManagementIncremental Update
Tencent Music Tech Team
Written by

Tencent Music Tech Team

Public account of Tencent Music's development team, focusing on technology sharing and communication.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.