編輯:關於Android編程
Android增量更新技術在很多公司都在使用,網上也有一些相關的文章,但大家可能未必完全理解實現的方式,本篇博客,我將一步步的帶大家實現增量更新。
當我們開發完一個項目之後,上線維護 , 增加新功能 , 添加第三方庫 , APK大小從4 - 5M , 增長到10+M , 用戶由原來的幾十秒下載 , 到現在幾分鐘以上的下載 , 網絡情況不好的時候 , 或許就是十分鐘不等。每次全量下載 , 無論從體驗還是流量上 , 都是不友好的 , 所有增量更新還是有必要的 (小公司好像沒幾個用 , 一般大公司在用,如QQ空間)。
說白了,增量更新就是:用戶手機上安裝著某個應用,下載了增量包,手機上的apk和增量包合並形成新的包,然後再次安裝(注意這個過程是要重新安裝的,當然部分應用市場有root權限你可能感知不到)。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxzdHJvbmc+yrXP1su8wrejujwvc3Ryb25nPsq508O/qtS0z+7Ev2JzZGlmZiC9+NDQzsS8/sTayN2xyL3Po6yyosfSyrnTw8HLYnppcDK9+NDQzsS8/tG5y/Ugo6wgy/nT0LXDs/a1xLLut9aw/L/JxNyxyMDtwtvWtdKq0KEgo6wgvfjSu7K9v8nS1Lz1ydnTw7unwffBvyCho9T2wb+4/NDCIKOsIL3Pzqq52Lz8tcSyv7fWvs3Kx8n6s8my7rfWsPwgo6wgvavQwr7JQVBLvfjQ0LHIvc8go6wgyfqzydK7uPbQwrXEzsS8/iChozwvcD4NCjxoMSBpZD0="需要使用的資源">需要使用的資源
進行增加更新主要是通過開源項目bsdiff項目來進行實現,還需要一些輔助的工具,列表如下:
bsdiff — bsdiff (win環境) 生成差分包及合並差分包庫 , 源碼內已包含bzip2
項目地址:
https://github.com/hymanAndroid/tools/tree/master/bsdiff-4.3
bzip2 — bzip2 bsdiff 依賴
服務器 — Tomcat 7.0 (模擬網絡環境)放置差分包 , 供APP下載
開發工具 — Eclipse NDK開發 , 目前建議使用Eclipse開發
開發工具 — VS 因為服務器搭建在windows平台 , 所以使用VS開發JNI
下載完bsdiff之後 , 我們看到如下目錄:
看這麼多文件 , 還有一些亂七八糟的不知道什麼的文件 , 那麼我們只關注 , 我們想要的文件 , 將C/C++源文件以及.h頭文件,找出來 ,放到一個干淨的文件夾中 。
1.使用visual studio去新建一個項目
2.將bsdiff中的.c和.cpp文件放到VS項目源文件中,把.h文件放到VS項目頭文件中
然後嘗試進行編譯,發現報錯,用到了過時的函數
錯誤內容是:使用了非安全的函數 , 解決方法是在文件中聲明#define _CRT_SECURE_NO_WARNINGS即可 。如果是多個文件都需要進行聲明,顯然一條條的聲明太麻煩了,所以可以在屬性中聲明,所有的文件都適用。
修改完畢,再次嘗試編譯,還是報錯,內容如下:
原因是在VS中通不過安全語法檢查 , 在VS中進行如下設置,將SDL檢查由“是”改為“否”:
還需要在文件中添加#define _CRT_NONSTDC_NO_DEPRECATE預處理指令。
然後我們需要生成.dll動態庫,需要我們在配置中,將“配置類型”改為“動態庫(.dll)”
編譯生成.dll動態庫後 , 賦值到服務器項目的目錄下 , 或是Java項目也可以 。
編譯生成.dll動態庫時,還可能出現一個坑,那就是:變量未初始化
u_char *old = nullptr,*_new = nullptr; off_t oldsize,newsize; off_t *I,*V = nullptr;
可能會是這幾個 , 將其賦值為nullptr就可以了。 如果還有其他錯誤 , 可執行分析 , google , 也歡迎評論留言 , 多多交流 。
3.了解bsdiff的用法 , 在main函數中尋找思路
int main(int argc,char *argv[]) { int fd; u_char *old,*_new; off_t oldsize,newsize; off_t *I,*V; off_t scan,pos,len; off_t lastscan,lastpos,lastoffset; off_t oldscore,scsc; off_t s,Sf,lenf,Sb,lenb; off_t overlap,Ss,lens; off_t i; off_t dblen,eblen; u_char *db,*eb; u_char buf[8]; u_char header[32]; FILE * pf; BZFILE * pfbz2; int bz2err; if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
在bsdiff.cpp文件找到帶參數的main函數 , 並且有一個關於用法的線索 , 那就是:
if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
我們可以根據這句話來推測 , 需要四個參數 , 並且三個參數必須傳入的文件路徑 , 剩下的一個參數不會有影響,直接傳入任何值都可以。
4.創建JNI方法 , 修改main函數
既然知道了需要傳入的參數 , 那麼就可以創建一個Java工程 , 編寫JNI方法了。
public class BsDiff { /** * 生成差分包 * * @param oldfile 老版本文件路徑 * @param newfile 新版本文件路徑 * @param patchfile 生成差分包文件路徑 */ public native static void diff(String oldfile, String newfile, String patchfile); static{ System.loadLibrary("bsdiff"); } }
接下來使用javah命令,生成頭文件 , 將.h頭文件拷貝到我們 VS的工程中,同時com_dispatch_bsdiff_BsDiff.h頭文件還需要依賴jni.h和jni_md.h兩個文件
com_dispatch_bsdiff_BsDiff.h中引入jni.h
將頭文件com_dispatch_bsdiff_BsDiff.h引入到bsdiff.cpp文件中
5.編寫調用的C函數 , 並修改main函數名稱,main函數作為入口函數 , 在JNI中就適用了 , 所有將main函數名稱改一下 , 在JNI的C函數中調用即可 。
編寫bsdiff.cpp的native函數
//JNI 調用 JNIEXPORT void JNICALL Java_com_dispatch_bsdiff_BsDiff_diff (JNIEnv *env, jclass jcls, jstring oldfile_jstr, jstring newfile_jstr, jstring patchfile_jstr){ int argc = 4; char* oldfile = (char*)env->GetStringUTFChars(oldfile_jstr, NULL); char* newfile = (char*)env->GetStringUTFChars(newfile_jstr, NULL); char* patchfile = (char*)env->GetStringUTFChars(patchfile_jstr, NULL); //參數(第一個參數無效) char *argv[4]; argv[0] = "bsdiff"; argv[1] = oldfile; argv[2] = newfile; argv[3] = patchfile; bsdiff_main(argc, argv); env->ReleaseStringUTFChars(oldfile_jstr, oldfile); env->ReleaseStringUTFChars(newfile_jstr, newfile); env->ReleaseStringUTFChars(patchfile_jstr, patchfile); }
一切修改完畢 ,重新編譯生成.dll動態庫 。
差分包工具類:
public class BsdiffUtils { /** * 生成差分包 * @param oldFilePath 老版本文件路徑 * @param newFilePath 新版本文件路徑 * @param patchFilePath 生成差分包文件路徑 */ public static native void diff(String oldFilePath , String newFilePath , String patchFilePath) ; }
使用差分包工具類:
public class BsdiffApk { public static void main(String args[]) { /** * 文件差分 */ BsdiffUtils.diff(Constract.OLD_FILE_PATH, Constract.NEW_FILE_PATH, Constract.PATCH_FILE_PATH); } }
常量類,主要用於存儲差分包路徑:
public class Constract { public static final String OLD_FILE_PATH = "E:/javaee_workspace/AppUpdateServer/apk/app-old.apk" ; public static final String NEW_FILE_PATH = "E:/javaee_workspace/AppUpdateServer/apk/app-new.apk" ; public static final String PATCH_FILE_PATH = "E:/javaee_workspace/AppUpdateServer/apk/App_patch.patch" ; }
生成差分包:
1.提取bzip2中的源文件
2.將bzip2加入到Android Studio項目中
首先將工程切換到Project模式 , 將bzip2文件夾復制到cpp目錄下 。因為最新的Android Studio采用的是CMake構建工具 , 所有需要在bzip2目錄下,創建一個CMakeLists.txt文件:
3.將bspatch.c復制到cpp目錄下 , 並將自動生成的CMakeList.txt文件拖拽到cpp目錄下 , 並添加子目錄參與編譯 。
修改了CMakeLists.txt文件的路徑之後 , 需要在build.gradle中修改一下配置了:
並且配置一下build環境
4.然後就是在java代碼中編寫合並差分文件的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"); } }
5.編寫C函數 , 怎樣找執行函數 , 和前面拆分文件的套路是一樣的
/*合並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__)
拆分和合並的代碼就已經寫完了,其余下載的java代碼,大家可以根據自己項目的情況去進行編寫,這裡就不再貼出
6.打包
因為Android Studio使用了instant run技術 , 所以使用Android Studio生成APK最好是打正式包 , 並且包中內容要有差異性 , 然後再生成差分包 , 直接放置在WEB項目的WebContent根目錄下即可 。
概述認識MVP模式MVP 模式實際上指的是 Model-View-Presenter 主要的目的是為了劃分各個模塊的負責區域,分工明確,使代碼清晰了很多。也是為了減少 A
ProGuard能夠對Java類中的代碼進行壓縮(Shrink),優化(Optimize),混淆(Obfuscate),預檢(Preveirfy)。 1. 壓縮(Sh
概述作為一個android開發者,在開發應用時,隨著業務規模發展到一定程度,不斷地加入新功能、添加新的類庫,代碼在急劇的膨脹,相應的apk包的大小也急劇增加,
本文要演示的Android開發實例是如何完成一個Android中的miniTwitter登錄界面,下面將分步驟講解怎樣實現圖中的界面效果,讓大家都能輕松的做出美觀的登錄界