[TOC]
1. 下载并配置NDK:
https://developer.android.com/ndk/downloads/index.html
2. 编译Bsdiff
2.1 编写java类,创建native方法
package com.commons.utils;
public class Bspatch {
static {
System.loadLibrary("bspatch");
}
public static native int applyPatch(String oldFile, String newFile, String patchFile);
}
2.2 生成.h文件
使用javac编写Bspatch或者在ide里面编译,这里我在android studio中编写并执行gradle的compileDebugJavaWithJavac任务进行编译,编译完成后,切换到classes目录。
使用javah生成.h文件
// 切换到编译好的类目录 cd app/build/intermediates/classes // 生成.h文件 javah -d h -classpath debug com.commons.utils.Bspatch
2.3 编译c/c++代码.
- 复制生成的.h文件到c/c++文件目录.
- 在存放c/c++代码的目录中添加Android.mk、Application.mk文件。 其中Android.mk用于指定要编译的类,模块名称等; Application.mk用于指定要生成特定abi类型的so文件,如:x86,armeabi等。
目录结构如下:
- app
- build
- jni
- bspatch
- bzip2
- Android.mk
- Application.mk
- com_commons_utils_Bspatch.c
- com_commons_utils_Bspatch.h
- libs
- src
- build.gradle
- proguard-rules.pro
Android.mk内容如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # 指定模块名称 LOCAL_MODULE := bspatch # 添加源码 LOCAL_SRC_FILES := com_commons_utils_BsPatch.c # for logging LOCAL_LDLIBS := -lz -llog include $(BUILD_SHARED_LIBRARY)
Application.mk
#表示生成全部abi的so APP_ABI := all
切换到jni目录,手动编译native代码。
$ cd app/jni $ ndk-build
3. 修改bspatch代码
为了调用简单,这里直接修改了bspatch的main函数为patch函数,然后在jni函数中直接调用patch函数进行合并。
#include <android/log.h> #include "bspatch/bspatch.c" #include "com_commons_utils_Bspatch.h" /* * Class: com_commons_utils_Bspatch * Method: applyPatch * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_com_commons_utils_Bspatch_applyPatch (JNIEnv *env, jobject obj, jstring oldFile, jstring newFile, jstring patchFile) { int ret; char *ch[4]; ch[0] = "bspatch"; ch[1] = (char *) ((*env)->GetStringUTFChars(env, oldFile, 0)); ch[2] = (char *) ((*env)->GetStringUTFChars(env, newFile, 0)); ch[3] = (char *) ((*env)->GetStringUTFChars(env, patchFile, 0)); __android_log_print(ANDROID_LOG_INFO, "bspatch", "oldFile = %s ", ch[1]); __android_log_print(ANDROID_LOG_INFO, "bspatch", "newFile = %s ", ch[2]); __android_log_print(ANDROID_LOG_INFO, "bspatch", "patchFile = %s ", ch[3]); ret = patch(4, ch); __android_log_print(ANDROID_LOG_INFO, "bspatch", "applypatch result = %d ", ret); (*env)->ReleaseStringUTFChars(env, oldFile, ch[1]); (*env)->ReleaseStringUTFChars(env, newFile, ch[2]); (*env)->ReleaseStringUTFChars(env, patchFile, ch[3]); return ret; }
另由于在bspatch的代码中使用了err、errx函数,会导致出错时直接退出进程。为了在app中调用bspatch时不出现这种情况,所以使用自定义的err、errx函数替换系统err、errx函数,同时以返回值确定是否合并成功。
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdarg.h> // #include <err.h> #include <unistd.h> #include <fcntl.h> #include <android/log.h> #include <jni.h> #include "../bzip2/bzlib.c" #include "../bzip2/crctable.c" #include "../bzip2/compress.c" #include "../bzip2/decompress.c" #include "../bzip2/randtable.c" #include "../bzip2/blocksort.c" #include "../bzip2/huffman.c" #include <android/log.h> // #define vprintf(...) __android_log_print(ANDROID_LOG_DEBUG, "bspatch", __VA_ARGS__); static int err(int retVal, char* fmt, ...) { va_list arglist; va_start(arglist, fmt); // vprintf(fmt, arglist); __android_log_print(ANDROID_LOG_DEBUG, "bspatch", fmt, arglist); va_end(arglist); return retVal; } static int errx(int retVal, char* fmt, ...) { va_list arglist; va_start(arglist, fmt); // vprintf(fmt, arglist); __android_log_print(ANDROID_LOG_DEBUG, "bspatch", fmt, arglist); va_end(arglist); return retVal; } static void cleanup(FILE* f, FILE* cpf, FILE* dpf, FILE* epf, BZFILE* cpfbz2, BZFILE* dpfbz2, BZFILE* epfbz2, u_char* old, u_char* new) { // close file and then free; if (f != NULL) { fclose(f); free(f); } if (cpf != NULL) { fclose(cpf); free(cpf); } if (dpf != NULL) { fclose(dpf); free(dpf); } if (epf != NULL) { fclose(epf); free(epf); } if (cpfbz2 != NULL) { BZ2_bzReadClose(NULL, cpfbz2); } if (dpfbz2 != NULL) { BZ2_bzReadClose(NULL, dpfbz2); } if (epfbz2 != NULL) { BZ2_bzReadClose(NULL, epfbz2); } if (old != NULL) { free(old); } if (new != NULL) { free(new); } } static off_t offtin(u_char *buf) { off_t y; y = buf[7] & 0x7F; y = y * 256; y += buf[6]; y = y * 256; y += buf[5]; y = y * 256; y += buf[4]; y = y * 256; y += buf[3]; y = y * 256; y += buf[2]; y = y * 256; y += buf[1]; y = y * 256; y += buf[0]; if (buf[7] & 0x80) y = -y; return y; } static int patch(int argc, char *argv[]) { FILE *f, *cpf, *dpf, *epf; BZFILE *cpfbz2, *dpfbz2, *epfbz2; int cbz2err, dbz2err, ebz2err; int fd; ssize_t oldsize, newsize; ssize_t bzctrllen, bzdatalen; u_char header[32], buf[8]; u_char *old, *new; off_t oldpos, newpos; off_t ctrl[3]; off_t lenread; off_t i; if (argc != 4) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "usage: %s oldfile newfile patchfile\n", argv[0]); } /* Open patch file */ if ((f = fopen(argv[3], "r")) == NULL) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "fopen(%s)", argv[3]); } /* File format: 0 8 "BSDIFF40" 8 8 X 16 8 Y 24 8 sizeof(newfile) 32 X bzip2(control block) 32+X Y bzip2(diff block) 32+X+Y ??? bzip2(extra block) with control block a set of triples (x,y,z) meaning "add x bytes from oldfile to x bytes from the diff block; copy y bytes from the extra block; seek forwards in oldfile by z bytes". */ /* Read header */ if (fread(header, 1, 32, f) < 32) { if (feof(f)) { errx(1, "Corrupt patch\n"); } cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "fread(%s)", argv[3]); } /* Check for appropriate magic */ if (memcmp(header, "BSDIFF40", 8) != 0) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "Corrupt patch\n"); } /* Read lengths from header */ bzctrllen = offtin(header + 8); bzdatalen = offtin(header + 16); newsize = offtin(header + 24); if ((bzctrllen < 0) || (bzdatalen < 0) || (newsize < 0)) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "Corrupt patch\n"); } /* Close patch file and re-open it via libbzip2 at the right places */ if (fclose(f)) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "fclose(%s)", argv[3]); } if ((cpf = fopen(argv[3], "r")) == NULL) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "fopen(%s)", argv[3]); } if (fseeko(cpf, 32, SEEK_SET)) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "fseeko(%s, %lld)", argv[3], (long long) 32); } if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err); } if ((dpf = fopen(argv[3], "r")) == NULL) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "fopen(%s)", argv[3]); } if (fseeko(dpf, 32 + bzctrllen, SEEK_SET)) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "fseeko(%s, %lld)", argv[3], (long long) (32 + bzctrllen)); } if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err); } if ((epf = fopen(argv[3], "r")) == NULL) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "fopen(%s)", argv[3]); } if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET)) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "fseeko(%s, %lld)", argv[3], (long long) (32 + bzctrllen + bzdatalen)); } if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err); } if (((fd = open(argv[1], O_RDONLY, 0)) < 0) || ((oldsize = lseek(fd, 0, SEEK_END)) == -1) || ((old = malloc(oldsize + 1)) == NULL) || (lseek(fd, 0, SEEK_SET) != 0) || (read(fd, old, oldsize) != oldsize) || (close(fd) == -1)) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "%s", argv[1]); } if ((new = malloc(newsize + 1)) == NULL) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, NULL); } oldpos = 0; newpos = 0; while (newpos < newsize) { /* Read control data */ for (i = 0; i <= 2; i++) { lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8); if ((lenread < 8) || ((cbz2err != BZ_OK) && (cbz2err != BZ_STREAM_END))) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "Corrupt patch\n"); } ctrl[i] = offtin(buf); }; /* Sanity-check */ if (newpos + ctrl[0] > newsize) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "Corrupt patch\n"); } /* Read diff string */ lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]); if ((lenread < ctrl[0]) || ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END))) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "Corrupt patch\n"); } /* Add old data to diff string */ for (i = 0; i < ctrl[0]; i++) if ((oldpos + i >= 0) && (oldpos + i < oldsize)) new[newpos + i] += old[oldpos + i]; /* Adjust pointers */ newpos += ctrl[0]; oldpos += ctrl[0]; /* Sanity-check */ if (newpos + ctrl[1] > newsize) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "Corrupt patch\n"); } /* Read extra string */ lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]); if ((lenread < ctrl[1]) || ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END))) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return errx(1, "Corrupt patch\n"); } /* Adjust pointers */ newpos += ctrl[1]; oldpos += ctrl[2]; }; /* Clean up the bzip2 reads */ BZ2_bzReadClose(&cbz2err, cpfbz2); BZ2_bzReadClose(&dbz2err, dpfbz2); BZ2_bzReadClose(&ebz2err, epfbz2); if (fclose(cpf) || fclose(dpf) || fclose(epf)) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "fclose(%s)", argv[3]); } /* Write the new file */ if (((fd = open(argv[2], O_CREAT | O_TRUNC | O_WRONLY, 0666)) < 0) || (write(fd, new, newsize) != newsize) || (close(fd) == -1)) { cleanup(f, cpf, dpf, epf, cpfbz2, dpfbz2, epfbz2, old, new); return err(1, "%s", argv[2]); } free(new); free(old); return 0; }