Android apk incremental upgrade

Source: Internet
Author: User

Android apk incremental upgrade
Preface

Although this article seems very simple, there are many problems encountered in the experiment. For example, the ndk compilation error under andorid studio, which is the final feasible method presented in this article.

The required resources bzip2 bsdiff ndk two different versions of the test apk principle on the server side, generate the latest version and all previous versions of the difference package, why is it all versions, because we cannot know what version the current user version is when the mobile client updates the program, download the differential package, use the installed old apk with this differential package, merge into a new apk to check whether the newly merged apk file is complete, and check whether the signature of the merged version is consistent with that of the installed client. If yes, the system prompts you to install and generate a differential package.

Download the dependent files required to generate the differential package

Bsdiff bzip2

Create a project and call the native code

Create a java layer class

package cn.edu.zafu.util;public class DiffAndPatch {    static {        System.loadLibrary(DiffAndPatch);    }    public static native int generateDiff(String oldApkPath, String newApkPath,            String patchPath);    public static native int applyPatch(String oldApkPath, String newApkPath,            String patchPath);}

Go to the bin directory of the project and run the javah command to create the header file.

javah cn.edu.zafu.util.DiffAndPatch

Copy the header file to the jni folder (created by yourself) and change the suffix to. c.
Clear the content and copy the following code.

#include cn_edu_zafu_util_DiffAndPatch.h#include 
  
   #ifdef __cplusplusextern C {#endif/* * Class:     cn_edu_zafu_util_DiffAndPatch * Method:    generateDiff * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I */JNIEXPORT jint JNICALL Java_cn_edu_zafu_util_DiffAndPatch_generateDiff  (JNIEnv *env, jclass arg, jstring old, jstring new, jstring patch){    printf(-----generateDiff start-----);    clock_t start, finish;    double duration;    int argc = 4;    char * argv[argc];    argv[0] = bsdiff;    argv[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0));    argv[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0));    argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));    printf(old apk = %s , argv[1]);    printf(new apk = %s , argv[2]);    printf(patch = %s , argv[3]);    printf(start to generate patch file);    start = clock();     int ret = generatePatch(argc, argv);    finish = clock();    printf(finish to generate patch file);    duration = (double)(finish - start) / CLOCKS_PER_SEC;    printf(generateDiff result = %d , ret);    printf(time spended %f s ,duration);    (*env)->ReleaseStringUTFChars(env, old, argv[1]);    (*env)->ReleaseStringUTFChars(env, new, argv[2]);    (*env)->ReleaseStringUTFChars(env, patch, argv[3]);    printf(-----generateDiff end-----);    return ret;}/* * Class:     cn_edu_zafu_util_PatchUtil * Method:    applyPatch * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I */JNIEXPORT jint JNICALL Java_cn_edu_zafu_util_DiffAndPatch_applyPatch  (JNIEnv *env, jclass arg, jstring old, jstring new, jstring patch){    printf(-----applyPatch start-----);    clock_t start, finish;    double duration;    int argc = 4;    char * argv[argc];    argv[0] = bspatch;    argv[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0));    argv[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0));    argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));    printf(old apk = %s , argv[1]);    printf(patch = %s , argv[3]);    printf(new apk = %s , argv[2]);    printf(start to apply patch file);    start = clock();    int ret = applyPatch(argc, argv);    finish = clock();    printf(finish to apply patch file);    duration = (double)(finish - start) / CLOCKS_PER_SEC;    printf(applyPatch result = %d , ret);    printf(time spended %f s ,duration);    (*env)->ReleaseStringUTFChars(env, old, argv[1]);    (*env)->ReleaseStringUTFChars(env, new, argv[2]);    (*env)->ReleaseStringUTFChars(env, patch, argv[3]);    printf(-----applyPatch end-----);    return ret;}#ifdef __cplusplus}#endif
  

Copy the dependent files to the jni directory, and find the corresponding files from the downloaded bsdiff and bzip2. The file list

Create a Makefile file under the jni file. The content is as follows. Change the jdk directory to the directory on your computer.

CC      := gccCFLAGS  := -I /opt/java/jdk1.8.0_25/include -I /opt/java/jdk1.8.0_25/include/linux -I /usr/includeLDFLAGS := -fPIC -shared SOURCE  := $(wildcard *.c *.h)libDiffAndPatch.so: $(SOURCE)      $(CC) $(LDFLAGS) $(CFLAGS) $^ -o libDiffAndPatch.soclean:    rm libDiffAndPatch.so

Add the native library location. Right-click the project file Propreties-> Java Build Path-> Libraryies-> JRE System Library-> Native Library Location, double-click it, and point it to the jni directory.

Run the command line to enter the jni directory for compilation.

Write the test code and place the apk file in the corresponding folder. The directory is/home/lizhangqu/apk /.

Package cn.edu. zafu; import cn.edu. zafu. util. diffAndPatch; public class Test {// old version public static final String OLD_APK =/home/lizhangqu/apk/ApkPatchTestVersion_1.apk; // New Version public static final String NEW_APK =/home/lizhangqu/apk/Repository; // patch storage path public static final String PATCH_FILE =/home/lizhangqu/apk/version_1_to_version_2 _. patch; // use the old version + patch package to synthesize the new version public static final String OLD_TO_NEW_APK =/home/lizhangqu/apk/generated.apk; public static void main (String [] args) {System. out. println (generating file. It may take some time. Please wait ...); diffAndPatch. generateDiff (OLD_APK, NEW_APK, PATCH_FILE );}}

The following information will be output:

Generating File. It may take some time. Please wait... ----- generateDiff start ----- old apk =/home/lizhangqu/apk/Latest new apk =/home/lizhangqu/apk/ApkPatchTestVersion_2.apk patch =/home/lizhangqu/apk/version_1_to_version_2 _. patch start to generate patch filefinish to generate patch filegenerateDiff result = 0 time queued ded 2.595576 s ----- generateDiff end -----

Install the old version on the mobile phone and copy the generated patch to the mobile phone. Copy the patch to the apk folder in the root path of the mobile phone memory card.

Synthesize and test the apk on the mobile phone end

Use android studio to create a project and create a java layer to call the native Layer Code

Package cn.edu. zafu. util;/*** class description: apk synthesis tool class ** @ author lizhangqu * @ date 2015-5-29 * @ version 1.0 */public class PatchUtil {static {System. loadLibrary (Patch );} /*** @ param oldApkPath old package path * @ param newApkPath generated package path * @ param patchPath patch path * @ return */public static native int applyPatch (String oldApkPath, string newApkPath, String patchPath );}

After the build, open the terminal in android studio and use the javah command to generate the header file.

cd app/src/mainjavah -d jni -classpath /media/lizhangqu/windows/sdk/platforms/android-22/android.jar:../../build/intermediates/classes/debug/ cn.edu.zafu.util.PatchUtil

Then, the header file is generated in the jni directory. copy the file and change it to. c. The implementation content is as follows:

#include 
  
   #include 
   
    JNIEXPORT jint JNICALL Java_cn_edu_zafu_util_PatchUtil_applyPatch(JNIEnv *env, jclass arg, jstring oldApk, jstring newApk, jstring patch){    printf(-----applyPatch start-----);    clock_t start, finish;    double duration;    int argc = 4;    char * argv[argc];    argv[0] = bspatch;    argv[1] = (char*) ((*env)->GetStringUTFChars(env, oldApk, 0));    argv[2] = (char*) ((*env)->GetStringUTFChars(env, newApk, 0));    argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));    printf(old apk = %s , argv[1]);    printf(patch = %s , argv[3]);    printf(new apk = %s , argv[2]);    printf(start to apply patch file);    start = clock();    int ret = applyPatch(argc, argv);    finish = clock();    printf(finish to apply patch file);    duration = (double)(finish - start) / CLOCKS_PER_SEC;    printf(applyPatch result = %d , ret);    printf(time spended %f s ,duration);    (*env)->ReleaseStringUTFChars(env, oldApk, argv[1]);    (*env)->ReleaseStringUTFChars(env, newApk, argv[2]);    (*env)->ReleaseStringUTFChars(env, patch, argv[3]);    printf(-----applyPatch end-----);    return ret;}
   
  

Copy all the files except the Makefile and self-generated files under the jni directory of the project that generated the differential package to the jni directory of android studio.

Create Android. mk in the jni file. The content is as follows:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := PatchLOCAL_SRC_FILES :=     blocksort.c    bspatch.c    bzip2.c    bzlib.c    bzlib.h    bzlib_private.h    cn_edu_zafu_util_PatchUtil.c    cn_edu_zafu_util_PatchUtil.h    compress.c    crctable.c    decompress.c    huffman.c    randtable.cinclude $(BUILD_SHARED_LIBRARY)

Create Application. mk in the jni directory. The content is as follows:

APP_ABI := armeabiAPP_PLATFORM :=android-15

APP_PLATFORM is required. Otherwise, an error will be reported during compilation.

Disable android studio's built-in ndk build and use your own Android. mk and Application. mk files.

Add the ndk directory to local. properties.

ndk.dir=/media/lizhangqu/windows/sdk/android-ndk-r10e

Modify the build. gradle file in the app directory. The final content is as follows:

import org.apache.tools.ant.taskdefs.condition.Osapply plugin: 'com.android.application'android {    compileSdkVersion 22    buildToolsVersion 22.0.1    defaultConfig {        applicationId cn.edu.zafu.patch        minSdkVersion 15        targetSdkVersion 22        versionCode 1        versionName 1.0    }    buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }    sourceSets.main.jni.srcDirs = [] // disable automatic ndk-build call, which ignore our Android.mk    sourceSets.main.jniLibs.srcDir 'src/main/libs'    // call regular ndk-build(.cmd) script from app directory    task ndkBuild(type: Exec) {        workingDir file('src/main')        commandLine getNdkBuildCmd()    }    tasks.withType(JavaCompile) {        compileTask -> compileTask.dependsOn ndkBuild    }    task cleanNative(type: Exec) {        workingDir file('src/main')        commandLine getNdkBuildCmd(), 'clean'    }    clean.dependsOn cleanNative}dependencies {    compile fileTree(dir: 'libs', include: ['*.jar'])    compile 'com.android.support:appcompat-v7:22.1.1'}def getNdkDir() {    if (System.env.ANDROID_NDK_ROOT != null)        return System.env.ANDROID_NDK_ROOT    Properties properties = new Properties()    properties.load(project.rootProject.file('local.properties').newDataInputStream())    def ndkdir = properties.getProperty('ndk.dir', null)    if (ndkdir == null)        throw new GradleException(NDK location not found. Define location with ndk.dir in the local.properties file or with an ANDROID_NDK_ROOT environment variable.)    return ndkdir}def getNdkBuildCmd() {    def ndkbuild = getNdkDir() + /ndk-build    if (Os.isFamily(Os.FAMILY_WINDOWS))        ndkbuild += .cmd    return ndkbuild}

Write the update code. Assume that the patch has been downloaded to the local device.

Package cn.edu. zafu; import android. OS. bundle; import android. OS. environment; import android. OS. handler; import android. OS. message; import android. support. v7.app. appCompatActivity; import android. util. log; import cn.edu. zafu. util. apkUtil; import cn.edu. zafu. util. patchUtil; import cn.edu. zafu. util. signUtil; public class MainActivity extends AppCompatActivity {private static final String ROOT_PATH = Environment. getExternalStorageDirectory (). getAbsolutePath (); private static final String PACKAGE_NAME = com. sina. weibo; // The storage path of the patch is public static final String PATCH_FILE =/apk/weibo. patch; // use the old version + patch package to synthesize the new public static final String OLD_TO_NEW_APK =/apk/generated.apk; @ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); boolean isInstalled = ApkUtil. isInstalled (getApplicationContext (), PACKAGE_NAME); if (isInstalled) {String source = ApkUtil. getSourceApkPath (getApplicationContext (), PACKAGE_NAME); Log. d (TAG, source); Log. d (TAG, ROOT_PATH + OLD_TO_NEW_APK); Log. d (TAG, ROOT_PATH + PATCH_FILE); applyPatch (source, ROOT_PATH + OLD_TO_NEW_APK, ROOT_PATH + PATCH_FILE);} private void applyPatch (final String oldApk, final String newApk, final String patch) {new Thread (new Runnable () {@ Override public void run () {Log. d (TAG, merging files. It may take some time. Please wait ...); patchUtil. applyPatch (oldApk, newApk, patch); String oldSign = SignUtil. getInstalledApkSignature (getApplicationContext (), PACKAGE_NAME); String newSign = SignUtil. getUnInstalledApkSignature (newApk); if (oldSign. equals (newSign) {Log. d (TAG, same signature); ApkUtil. installApk (MainActivity. this, newApk);} else {Log. d (TAG, signature error );}}}). start ();}}

The tool code will not be pasted, and its function will be the same as the name. Remember to add the permission to read the memory card.

At this time, build the program and run the program again. Remember to put the patch file under the corresponding file directory. In a few seconds, the new package will be merged and installed.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.