接續(xù)上篇NDK開發(fā)基礎(chǔ)③增量更新之服務(wù)器端生成差分包
前情提要
增量更新原理就是在服務(wù)器端使用bsdiff進行文件內(nèi)容比較,再使用了bzip2進行文件壓縮 肌索, 在下載APP時可以減少用戶流量 。在客戶端 特碳, 則是將下載好的拆分包與現(xiàn)有的APK進行文件合并 诚亚, 得出新的APK晕换, 再進行安裝 。
生產(chǎn)資源及工具
bsdiff --- bsdiff 生成差分包及合并差分包庫 站宗, 使用bspatch.c
文件
bzip2 --- bzip2 bsdiff 依賴
服務(wù)器 --- Tomcat 7.0 (模擬網(wǎng)絡(luò)環(huán)境)放置差分包 闸准, 供APP下載
開發(fā)工具 --- Android Studio 2.2RC2 NDK開發(fā)
一 , 合并差分包
Ⅰ 提取bzip2中的源文件
Ⅱ 將
bzip2
加入到Android Studio項目中
首先將工程切換到Project模式 梢灭, 將bzip2
文件夾復(fù)制到cpp目錄下 夷家。因為最新的Android Studio采用的是CMake構(gòu)建工具 , 所有需要在bzip2
目錄下敏释,創(chuàng)建一個CMakeLists.txt
文件:
Ⅲ 將
bspatch.c
復(fù)制到cpp目錄下 库快, 并將自動生成的CMakeList.txt
文件拖拽到cpp目錄下 , 并添加子目錄參與編譯 钥顽。
修改了CMakeLists.txt
文件的路徑之后 义屏, 需要在build.gradle
中修改一下配置了:
并且配置一下build環(huán)境
Ⅳ 編寫JNI
public class BspatchJNI {
/**
* 合并增量文件
* @param oldFilePath 當前APK路徑
* @param newFilePath 合成后的新的APK路徑
* @param patchFilePath 增量文件路徑
*/
public static native void bspatchJNI(String oldFilePath,String newFilePath,String patchFilePath) ;
static {
System.loadLibrary("bspatch");
}
}
Ⅴ 編寫C函數(shù) , 怎樣找執(zhí)行函數(shù) 蜂大, 上一篇已經(jīng)說了 闽铐, 套路都是一樣的 。
/*合并APK*/
JNIEXPORT void JNICALL
Java_com_zeno_incrementupdate_ndk_BspatchJNI_bspatchJNI(JNIEnv *env, jclass type,
jstring oldFilePath_, jstring newFilePath_,
jstring patchFilePath_) {
const char *oldFilePath = (*env)->GetStringUTFChars(env, oldFilePath_, 0);
const char *newFilePath = (*env)->GetStringUTFChars(env, newFilePath_, 0);
const char *patchFilePath = (*env)->GetStringUTFChars(env, patchFilePath_, 0);
// if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
int argc = 4 ;
char* argv[4] ;
argv[0] = "bspatch";
argv[1] = oldFilePath;
argv[2] = newFilePath;
argv[3] = patchFilePath;
bspatch_main(argc,argv);
LOGE("MainActivity","%s","合并APK完成");
(*env)->ReleaseStringUTFChars(env, oldFilePath_, oldFilePath);
(*env)->ReleaseStringUTFChars(env, newFilePath_, newFilePath);
(*env)->ReleaseStringUTFChars(env, patchFilePath_, patchFilePath);
}
需要注意的時 县爬, 在bspatch.c
中是需要引入bzip2
的 阳啥, 所有需要在文件頭部, 引入bzip2
:
// bzip2
#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"
#define LOGE(TAG,FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,TAG,FORMAT,__VA_ARGS__)
Ⅵ 使用
class ApkUpdateTask extends AsyncTask<Void, Void, Boolean> {
@Override
protected Boolean doInBackground(Void... params) {
try {
//1.下載差分包
Log.e(TAG, "doInBackground: 正在下載财喳。察迟。。耳高。" );
File patchFile = DownloadUtils.download(Constants.URL_PATCH_DOWNLOAD);
//獲取當前應(yīng)用的apk文件/data/app/app
String oldFile = APKUtils.getSourceApkPath(MainActivity.this, getPackageName());
//2.合并得到最新版本的APK文件
String newApkPath = Constants.NEW_APK_PATH;
String patchFileAbsolutePath = patchFile.getAbsolutePath();
BspatchJNI.bspatchJNI(oldFile, newApkPath, patchFileAbsolutePath);
Log.d(TAG, "oldfile:"+oldFile);
Log.d(TAG, "newfile:"+newApkPath);
Log.d(TAG, "patchfile:"+patchFileAbsolutePath);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setTitle("正在下載...");
progressDialog.show();
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
progressDialog.dismiss();
//3.安裝
if(result){
Toast.makeText(MainActivity.this, "您正在進行無流量更新", Toast.LENGTH_SHORT).show();
APKUtils.installApk(MainActivity.this, Constants.NEW_APK_PATH);
}
}
}
使用起來都比較簡單 扎瓶, 這里就不將代碼貼全了,篇末會給出github地址泌枪。
Ⅶ 打包
因為Android Studio使用了instant run
技術(shù) 概荷, 所以使用Android Studio生成APK最好是打正式包 , 并且包中內(nèi)容要有差異性 碌燕, 然后再生成差分包 误证, 直接放置在WEB項目的WebContent
根目錄下即可 。
結(jié)語
增量更新 修壕, 從服務(wù)器端到客戶端實現(xiàn) 愈捅, 要寫的代碼其實不多 , 關(guān)鍵在于使用第三方C/C++源碼的套路 慈鸠, 使用JNI技術(shù)調(diào)用C/C++函數(shù) 蓝谨, 其關(guān)鍵點就是找執(zhí)行函數(shù),通常為main
函數(shù) 。NDK開發(fā)基礎(chǔ) 譬巫, 這一篇算是結(jié)尾 咖楣, 新版的Android Studio的NDK支持比較完善 , 使用了CMake進行項目構(gòu)建 芦昔,語法高亮以及語法提示 诱贿, 都做得相當?shù)暮昧?。開始下一個系列 烟零, C++開發(fā) 瘪松。
源碼
參考
CMake Practice 百度網(wǎng)盤 密碼: 58a3