首先須要明確,Android增量更新與熱修復是不一樣的技術概念。html
熱修復通常是用於當已經發布的app有Bug須要修復的時候,開發者修改代碼併發布補丁,讓應用可以在不須要從新安裝的狀況下實現更新,主流方案有Tinker、AndFix等。java
而增量更新的目的是爲了減小更新app所須要下載的包體積大小,常見如手機端遊戲,apk包體積爲幾百M,但有時更新只需下載十幾M的安裝包便可完成更新。c++
增量更新原理簡單來說,就是經過二進制算法找出新版本和舊版本安裝包的差別,並將差別提取出來生成所謂的補丁包(差分包)。移動端檢查更新時,只須要向服務器申請下載相對應的補丁包,而後將差分包與自身舊版本apk合併生成最新版本的apk。最終安裝合併生成的apk便可完成版本更新。git
功能的實現主要是藉助第三方差分工具,至關於作記錄,方便之後回憶。github
增量更新主要有兩個過程,第一是後臺將最新的apk(v1.0.1)與舊版本(v1.0.0)作對比,並生成差分包。第二步是移動端下載相對應的差分包,並在本地合併生成完整的最新apk,並引導用戶安裝。算法
對於apk包的差分和合並,須要藉助bsdiff工具來實現bash
下載bsdiff_win_exe.zip並解壓到本地服務器
其中bsdiff.exe是window用來生成差分包的工具,而bspatch是用來合成差分包的工具併發
首先,分別打一箇舊的apk(v1.0.0.apk),和一個新的apk(v1.0.1.apk),並存放在以前解壓的目錄。app
而後打開Windows命令行工具,切換到該目錄,打入命令bsdiff v1.0.0.apk v1.0.1.apk patch.patch
就能在目錄下看見生成的差分包patch.patch
保持在當前目錄,命令行輸入bspatch v1.0.0.apk new.apk patch.patch
就能在當前目錄下看見合成的完整安裝包new.apk
生成的new.apk與v1.0.1.apk是徹底相同的,安裝便可。
上述過程是爲了演示整個流程,如今要應用到Android端。Android移動端在增量更新過程當中,只負責下載和合並差分包,並引導用戶安裝。所以假設已經下載好對應的差分包。主要須要三步:第一是獲取自己舊apk路徑;第二是將差分工具集成到Android端;第三是調用方法合併差分包並引導用戶安裝。
//獲取如今運行的apk路徑
String oldApk = getApplicationInfo().sourceDir;
複製代碼
bsdiff開源工具源碼爲.c文件,即Android端須要配置JNI(如下是基於CMake)。
爲方便集成,已將所需功能打成so包,能夠直接導入so包,跳過此步驟。百度網盤連接(提取碼:8ia7)
bsdiff源碼依賴bszip,因此須要同時下載二者的源碼;或者百度網盤下載(提取碼:psbu)
(1)修改bzlib.h的導入聲明:打開bspatch.c文件,修改頭文件
(2)找到main方法,並將其更名爲execute_patch
cmake_minimum_required(VERSION 3.4.1)
# 查找文件系統中指定模式的路徑,如/* 匹配根目錄的文件(注意路徑)
file(GLOB bzip_source ${CMAKE_SOURCE_DIR}/bzip/*.c)
# 設置本地動態庫 編譯生成動態庫
add_library(
#模塊名
native-lib
# 動態庫/分享能夠
SHARED
#源文件
native-lib.cpp
#配置相應的文件引用
bspatch.c
${bzip_source}
)
find_library(
log-lib
log)
target_link_libraries(
native-lib
${log-lib})
複製代碼
extern "C" {
extern int execute_patch(int argc, char *argv[]);
}
extern "C"
JNIEXPORT void JNICALL Java_com_example_myapplication_utilities_BsPatchUtil_patch(JNIEnv *env, jobject instance, jstring oldApk_, jstring patch_, jstring output_) {
//將java字符串轉換成char指針
const char *oldApk = env->GetStringUTFChars(oldApk_, 0);
const char *patch = env->GetStringUTFChars(patch_, 0);
const char *output = env->GetStringUTFChars(output_, 0);
//bspatch ,oldfile ,newfile ,patchfile
char *argv[] = {"", const_cast<char *>(oldApk), const_cast<char *>(output),
const_cast<char *>(patch)};
execute_patch(4, argv);
// 釋放相應的指針gc
env->ReleaseStringUTFChars(oldApk_, oldApk);
env->ReleaseStringUTFChars(patch_, patch);
env->ReleaseStringUTFChars(output_, output);
}
複製代碼
注意:Java_com_example_myapplication_utilities_BsPatchUtil_patch
方法名須要根據實際定義更改
定義個BsPatchUtil
類,調用c代碼:(該方法名因與上面提到的Java_com_example_myapplication_utilities_BsPatchUtil_patch
方法名相對應)
public class BsPatchUtil {
static {
System.loadLibrary("native-lib");
}
/** * @param oldApkPath 舊apk文件路徑 * @param newApkPath 新apk文件路徑 * @param patchPath 生成的差分包的存儲路徑 */
public static native void patch(String oldApkPath, String newApkPath, String patchPath);
}
複製代碼
//已安裝的舊apk的路徑
val oldApk = applicationInfo.sourceDir
// 差分包的路徑
val patch = ...
// 合成以後的新apk的存儲路徑
val output = ...
//合成apk包
BsPatchUtil.patch(oldApk, patch, output)
複製代碼
合成差分包須要在子線程中運行,防止阻塞主線程。最終引導用戶安裝新生成的apk便可。