
1、概述
Android很多應(yīng)用沒有使用到NDK開發(fā),但想要開發(fā)更高級的應(yīng)用,NDK的學習是必然之路。NDK的好處不多說,這里也應(yīng)該說是JNI的好處,其中之一就是可以方便使用到C/C++世界里面的優(yōu)秀開源庫,這里要實戰(zhàn)的是增量更新,其中用到的是bsdiff開源代碼,而bsdiff又依賴bzip2開源代碼。
一開始自己做過一些硬件開發(fā),也使用過一些so庫,使用的話按照文檔指示一般沒什么問題,但實際上對NDK開發(fā)流程總有一些疑問。比如說
- 如何創(chuàng)建so庫,so庫是怎么生成的?
- 假如我生成了so庫,如何給其他應(yīng)用調(diào)用這個so庫?
- jni目錄、cpp目錄又是什么?為什么有些項目使用的不一樣
- Android.mk、Application.mk是什么?
- NDK開發(fā)一定要使用到j(luò)avah、ndk-build命令嗎?
- CMake、CMakeLists.txt又是什么?
對這些問題同樣存在疑問的,可以參考網(wǎng)上的一些博客,寫的非常詳細
Android NDK 開發(fā)從 0 到 1
Android NDK開發(fā)掃盲及最新CMake的編譯使用
AndroidStudio中使用JNI/NDK示例
產(chǎn)生這些問題的原因在于平時只看書、博客是會忽略掉很多細節(jié),從而產(chǎn)生這樣的疑問,所以NDK的學習之路,必然需要動手操作。
增量更新不同于熱更新,增量更新可以應(yīng)用于app市場,避免用戶用過多的流量去升級app,只需要下載差分部分的patch補丁就可以,更快速、節(jié)省流量的實現(xiàn)了app的更新。
2、在mac上實現(xiàn)增量更新
我用的是mac系統(tǒng),win應(yīng)該也差不多,先下載文件
Binary diff/patch utility
bzip2
2.1、使用make命令生成bsdiff、bspatch可執(zhí)行文件
lexdeMacBook-Pro:bsdiff-4.3 lex$ make
遇到問題
Mac下xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
解決方法
xcode-select --install
重新安裝xcode-select就可以
執(zhí)行make命令后還是出現(xiàn)以下問題
Makefile:13: *** missing separator. Stop.
參考網(wǎng)上說對Makefile文件中 if/endif 做了縮進,沒效果。這里是一個坑,我用AndroidStudio去編輯無效,后來使用vim來編輯就可以了。

這時候生成了bsdiff可執(zhí)行文件,接著出現(xiàn)以下錯誤,無法生成bspatch可執(zhí)行文件,雖然說合并這部分在Android上做就可以了。
cc -O3 -lbz2 bsdiff.c -o bsdiff
cc -O3 -lbz2 bspatch.c -o bspatch
bspatch.c:39:21: error: unknown type name 'u_char'; did you mean 'char'?
static off_t offtin(u_char *buf)
^~~~~~
char
bspatch.c:65:8: error: expected ';' after expression
u_char header[32],buf[8];
^
;
bspatch.c:65:2: error: use of undeclared identifier 'u_char'; did you mean
'putchar'?
u_char header[32],buf[8];
^~~~~~
putchar
/usr/include/stdio.h:261:6: note: 'putchar' declared here
int putchar(int);
^
bspatch.c:65:9: error: use of undeclared identifier 'header'
u_char header[32],buf[8];
^
bspatch.c:65:20: error: use of undeclared identifier 'buf'
u_char header[32],buf[8];
...
缺少頭文件,編輯bspatch.c加入頭文件可以解決
#include <sys/types.h>
該文件是存在于
/usr/include/sys/types.h
再次執(zhí)行make命令,成功的生成了兩個可執(zhí)行文件bsdiff、bspatch,前者用于差分生成補丁包,后者用于合并補丁生成新的apk包。
2.2、增量文件的生成與合并
使用AndroidStudio簡單生成兩個apk,old.apk、new.apk
注意:每次生成apk最好clean一次工程
使用以下命令./bsdiff old.apk new.apk patch.patch
lexdeMacBook-Pro:bsdiff-4.3 lex$ ./bsdiff old.apk new.apk patch.patch
得到了patch.patch補丁
然后使用以下命令./bspatch old.apk new2.apk patch.patch
lexdeMacBook-Pro:bsdiff-4.3 lex$ ./bspatch old.apk new2.apk patch.patch
得到了新的apk,new2.apk
這個時候可以安裝查看,也可以使用MD5來校驗new.apk、new2.apk是否一致
lexdeMacBook-Pro:bsdiff-4.3 lex$ md5 new.apk
MD5 (new.apk) = 322b5a702bc1507e547c24f05109d812
lexdeMacBook-Pro:bsdiff-4.3 lex$ md5 new2.apk
MD5 (new2.apk) = 322b5a702bc1507e547c24f05109d812
事實上已經(jīng)可以說明兩個文件幾乎一致了,說明這個操作是可行的了。
3、Android上實現(xiàn)增量更新
但這個怎么在Android上使用呢?這時候就要使用到NDK的知識了。
流程應(yīng)該是這樣的:
① 服務(wù)器端生成patch補丁
② 客戶端通過對應(yīng)的版本號獲取相應(yīng)的patch補丁
③ 客戶端將本地apk跟patch補丁合并,生成新的apk
④ 安裝新的apk從而實現(xiàn)了增量更新
這里作為demo的話,就只要實現(xiàn)將本地apk跟patch補丁合并生成新apk,然后安裝看看是否成功就可以了。
3.1、新建一個C++ support工程
具體步驟不贅述,AndroidStudio默認使用CMake的形式
不要忘記添加SD卡讀寫權(quán)限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
3.2、NDK相關(guān)代碼的配置
copy相關(guān)文件到工程jni目錄下
復(fù)制bspatch.c到j(luò)ni目錄下
復(fù)制bzip2文件夾到j(luò)ni目錄下,只留下頭文件.h和源文件.c

修改CMakeLists.txt,主要是添加了需要編譯的.c源碼和.h頭文件,這里要全部都編譯,所以全部都添加到編譯列表里面去
cmake_minimum_required(VERSION 3.4.1)
add_library(
SHARED
src/main/jni/bspatch.c
src/main/jni/bzip2/blocksort.c
src/main/jni/bzip2/bzip2.c
src/main/jni/bzip2/bzip2recover.c
src/main/jni/bzip2/bzlib.c
src/main/jni/bzip2/bzlib.h
src/main/jni/bzip2/bzlib_private.h
src/main/jni/bzip2/compress.c
src/main/jni/bzip2/crctable.c
src/main/jni/bzip2/decompress.c
src/main/jni/bzip2/dlltest.c
src/main/jni/bzip2/huffman.c
src/main/jni/bzip2/mk251.c
src/main/jni/bzip2/randtable.c
src/main/jni/bzip2/spewG.c
src/main/jni/bzip2/unzcrash.c )
find_library(
log-lib
log )
target_link_libraries(
bspatch
${log-lib} )
修改bspatch.c里面的include,以及include jni
#include "bzip2/bzlib.h"
#include <jni.h>
編寫增量更新工具類BsPatchUtil.java,寫native方法
/**
* 增量更新工具類
* Created by lex.
*/
public class BsPatchUtil {
static {
System.loadLibrary("bspatch");
}
public static native int bspatch(String oldApk, String newApk, String patch);
}
選中bspatch方法,按alt + enter,生成源代碼方法,編寫jni代碼,傳入?yún)?shù)。patchMethod()就是bspatch.c中的main()方法
JNIEXPORT jint JNICALL
Java_com_example_lex_bsdiff_BsPatchUtil_bspatch(JNIEnv *env, jclass type, jstring oldApk_, jstring newApk_, jstring patch_) {
const char *oldApk = (*env)->GetStringUTFChars(env, oldApk_, 0);
const char *newApk = (*env)->GetStringUTFChars(env, newApk_, 0);
const char *patch = (*env)->GetStringUTFChars(env, patch_, 0);
// TODO
int argc = 4;
char *argv[argc];
argv[0] = "bspatch";
argv[1] = (char *) oldApk;
argv[2] = (char *) newApk;
argv[3] = (char *) patch;
int ret = patchMethod(argc, argv);
(*env)->ReleaseStringUTFChars(env, oldApk_, oldApk);
(*env)->ReleaseStringUTFChars(env, newApk_, newApk);
(*env)->ReleaseStringUTFChars(env, patch_, patch);
return ret;
}
編譯后會發(fā)現(xiàn),報main方法重復(fù)的錯誤
Error:(70) multiple definition of `main'
找到各個對應(yīng)文件,找到main方法,注釋掉就可以編譯成功了。
3.3、編寫Android代碼實現(xiàn)增量更新
/**
* 點擊按鈕觸發(fā)本地apk與補丁的合并
*/
public void patch(View btn) {
String oldApk = getSourceDir();
String newApk = getNewApkPath();
String patch = getPatchPath();
// 注意文件判空,否則崩潰
BsPatchUtil.bspatch(oldApk, newApk, patch);
// 如果成功了,調(diào)用intent去自動去安裝apk
// ...
}
/**
* 獲取本地apk的路徑
*/
public String getSourceDir() {
String sourceDir = getApplicationInfo().sourceDir;
Log.i(TAG, "sourceDir = " + sourceDir);
return sourceDir;
}
/**
* 生成新apk的路徑
*/
public String getNewApkPath() {
String newApkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "newApk.apk";
Log.i(TAG, "newApkPath = " + newApkPath);
return newApkPath;
}
/**
* 獲取補丁patch的路徑
*/
public String getPatchPath() {
String patch = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "patch.patch";
Log.i(TAG, "patch = " + patch);
return patch;
}
安裝新的apk,成功!
源碼已上傳到 github
感謝鴻洋_大神的 Android 增量更新完全解析 是增量不是熱修復(fù)