Android--incremental update

1. Introduction        

        When we released a new version, some users were not very active in upgrading, which caused the upgrade rate of the new version to be not high. In order to solve this problem, Google proposed Smart App Update, which is an incremental update (also called a differential update).

The process of incremental update is: an application is installed on the user's mobile phone, the incremental package is downloaded, the apk and the incremental package on the mobile phone are combined to form a new package, and then installed again (note that this process requires reinstallation, of course Some app markets have root permissions, you may not be aware of them).

Then refine the whole process into a few key points:

  1. Extract the apk of the currently installed application on the user's mobile phone
  2. How to use old.apk and new.apk to generate incremental files
  3. Add files and merge with old.apk in 1, then install

After solving the above 3 problems, it is ok.

The above two problems are solved with the help of the open source library bsdiff . First, let's demonstrate the formation and merging of differential packets.

Download bsdiff_win_exe.zip and extract it locally. As shown below: 
write picture description here

Then, we first type out an installation package, assuming old.apk. After modifying the source code, type a new installation package new.apk. Here old.apk is equivalent to the old version of the application, and new.apk is equivalent to the new version of the application. Next, we use bsdiff to generate the differential packet patch.patch.

Generate differential packets

Put the above old.apk and new.apk into the decompressed directory of bsdiff, and then execute the command in the console. After a while, the bsdiff old.apk new.apk patch.patchdifferential package patch.patch can be generated, as follows

List content

Merge differential packets

Merge old.apk and patch.patch to generate a new installation package new.apk. As long as the new.apk merged here is the same as the new.apk we typed above, then it can be considered as the new version installation package we need.

Let's see how to merge. Put old.apk and patch.patch into the bsdiff folder, before merging: 
write picture description here

Then execute the command bspatch old.apk new.apk patch.patch, and after a while, you can see the merged new.apk. As follows: 
write picture description here

Not surprisingly, the merged new.apk should be exactly the same as the new.apk we typed ourselves, which can be determined by verifying the md5 of the two.

The overall client-side support for incremental updates is similar to the above demo. The only difference is that the client needs to compile bspatch.c by itself to merge differential packages, which is called ndk development.

Use a third-party library for merging: https://github.com/cundong/SmartAppUpdates , the source code of bspatch has been added to jni. Just download it and compile it, and you can embed bspatch in your app for incremental updates.

After configuring the NDK, execute ndk-build in the SmartAppUpdates directory:

\ApkPatchLibrary\app\src\main\jni>ndk-build
[arm64-v8a] Compile        : ApkPatchLibrary <= com_cundong_utils_PatchUtils.c
[arm64-v8a] SharedLibrary  : libApkPatchLibrary.so
[arm64-v8a] Install        : libApkPatchLibrary.so => libs/arm64-v8a/libApkPatchLibrary.so
[x86_64] Compile        : ApkPatchLibrary <= com_cundong_utils_PatchUtils.c
[x86_64] SharedLibrary  : libApkPatchLibrary.so
[x86_64] Install        : libApkPatchLibrary.so => libs/x86_64/libApkPatchLibrary.so
[mips64] Compile        : ApkPatchLibrary <= com_cundong_utils_PatchUtils.c
[mips64] SharedLibrary  : libApkPatchLibrary.so
[mips64] Install        : libApkPatchLibrary.so => libs/mips64/libApkPatchLibrary.so
[armeabi-v7a] Compile thumb  : ApkPatchLibrary <= com_cundong_utils_PatchUtils.c
[armeabi-v7a] SharedLibrary  : libApkPatchLibrary.so
[armeabi-v7a] Install        : libApkPatchLibrary.so => libs/armeabi-v7a/libApkPatchLibrary.so
[armeabi] Compile thumb  : ApkPatchLibrary <= com_cundong_utils_PatchUtils.c
[armeabi] SharedLibrary  : libApkPatchLibrary.so
[armeabi] Install        : libApkPatchLibrary.so => libs/armeabi/libApkPatchLibrary.so
[x86] Compile        : ApkPatchLibrary <= com_cundong_utils_PatchUtils.c
[x86] SharedLibrary  : libApkPatchLibrary.so
[x86] Install        : libApkPatchLibrary.so => libs/x86/libApkPatchLibrary.so
[mips] Compile        : ApkPatchLibrary <= com_cundong_utils_PatchUtils.c
[mips] SharedLibrary  : libApkPatchLibrary.so
[mips] Install        : libApkPatchLibrary.so => libs/mips/libApkPatchLibrary.so
\ApkPatchLibrary\app\src\main\jni>

At this point, we can get the library named libApkPatchLibrary.so, which can be called through the com.cundong.utils.PatchUtils module.

public class PatchUtils {
	/**
	 * The native method uses the apk whose path is oldApkPath and the patch package whose path is patchPath to synthesize a new apk and store it in newApkPath
	 *
	 * Return: 0, indicating that the operation was successful
	 *
	 * @param oldApkPath Example: /sdcard/old.apk
	 * @param newApkPath Example: /sdcard/new.apk
	 * @param patchPath Example: /sdcard/xx.patch
	 * @return
	 */
	public static native int patch(String oldApkPath, String newApkPath,
			String patchPath);
}

The corresponding jni code being called is:

/*
 * Class: com_cundong_utils_PatchUtils
 * Method:    patch
 * Signature: (Ljava / lang / String; Ljava / lang / String; Ljava / lang / String;) I
 */
JNIEXPORT JNICALL Java_com_cundong_utils_PatchUtils_patch (JNIEnv * env,
		jobject obj, jstring old, jstring new, jstring patch) {
	char * ch[4];
	ch[0] = "bspatch";
	ch[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0));
	ch[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0));
	ch[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));
	__android_log_print(ANDROID_LOG_INFO, "ApkPatchLibrary", "old = %s ", ch[1]);
	__android_log_print(ANDROID_LOG_INFO, "ApkPatchLibrary", "new = %s ", ch[2]);
	__android_log_print(ANDROID_LOG_INFO, "ApkPatchLibrary", "patch = %s ", ch[3]);
	int ret = applypatch(4, ch);
	__android_log_print(ANDROID_LOG_INFO, "ApkPatchLibrary", "applypatch result = %d ", ret);
	(*env)->ReleaseStringUTFChars(env, old, ch[1]);
	(*env)->ReleaseStringUTFChars(env, new, ch[2]);
	(*env)->ReleaseStringUTFChars(env, patch, ch[3]);
	return ret;
}

In fact, the applypatch function is the main function of the bspatch code, and it has changed its name here.

Now, in the Demo project of SmartAppUpdates, we test whether the bspatch code added to the App can work normally: copy the old.apk and patch package just now to the /sdcard/ directory of the mobile phone, and add the following code to the onCreate() of the App within the method.

PatchUtils.patch(Environment.getExternalStorageDirectory().getPath() + "/old.apk",
        Environment.getExternalStorageDirectory().getPath() + "/out.apk",
        Environment.getExternalStorageDirectory().getPath() + "/patch");

After running the APP, the out.apk will be generated in the /sdcard/ directory as expected. Execute the command in the phone through adb shell:

/sdcard $ md5sum out.apk
cbb1afdbc32e4d1c62c4d38674a6a3a9  out.apk

It can be seen that the bspatch embedded in the app program also successfully passed the old apk and the patch package to generate a new apk, and the MD5 value of the generated out.apk is consistent with the new.apk. Test passed.

There is also a framework: https://github.com/ha-excited/BigNews

No need to download this project, add the code in your project root build.gradle:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Add code to build.gradle in your project module, then Gradle Sync:

    dependencies {
        compile 'com.github.ha-excited:BigNews:0.1.2'
    }

Call method :

Merge: Merge and upgrade from the differential package/upgrade package and the old installation package to the new installation package, and the new installation package is placed in newApkPath.

/** 
* oldApkPath: old installation package path 
* newApkPath: new installation package path (output) 
* patchPath: upgrade/differential package path 
* return: return true if successful, otherwise false. 
*/ BigNews.make (oldApkPath , newApkPath, patchPath); 


There is also a third-party library: https://github.com/jiyouliang2/SmartUpdateDemo

1. Add the following code to the project's build.gradle (as shown below)

allprojects {
    repositories {
        maven { url "https://jitpack.io" }
    }
}

2. Add dependencies in Module's build.gradle (as shown below)

compile 'com.github.jiyouliang2:SmartUpdateDemo:1.0.1'

3. Add permissions

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

4. Use in code

 PatchUtil.patch(旧版本, 新版本, 差分包);


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325887223&siteId=291194637