最近想着Android有沒有什麼熱更新檔更新技術的實作,于是上網搜尋。接着實作了這一功能後,記錄一下,免得自己忘了,可能文章會有出錯的地方,望指出,呵呵
搜尋發現有這3種方式可以實作(至于其他的方式,暫不清楚)
1.dexposed github https://github.com/alibaba/dexposed
2.andfix github https://github.com/alibaba/AndFix
3.bsdiff http://blog.csdn.net/lazyer_dog/article/details/47173013
dexposed和andfix是alibaba的開源項目,都是apk增量更新的實作架構,目前dexposed的相容性較差,隻有2.3,4.0~4.4相容,其他Android版本不相容或未測試,詳細可以去dexposed的github項目首頁檢視,而andfix則相容2.3~6.0,是以就拿這個項目來實作增量更新吧。至于bsdiff,隻是閱覽了一下,還沒研究過。
首先 git clone github https://github.com/alibaba/AndFix,将andfix項目下載下傳下來,Android studio可以在build.gradle裡導入andfix,
compile 'com.alipay.euler:andfix:[email protected]'
但是我是使用module的方式添加andfix,這樣可以直接檢視編輯源碼,而且直接gradle導入的話還有個問題,後面再說。
andfix項目裡有sample,導入也行,自己建立也行,我是自己建立項目,接着導入andfix作為module,demo裡就兩個類,mainactivity和myapplication
andfix裡有些檔案夾不用導入的,例如tools,doc等,記得建立jniLibs檔案夾,libs裡的so檔案移到jniLibs裡。
public class MainApplication extends Application {
private static final String TAG = "euler";
private static final String APATCH_PATH = "/out.apatch";
private static final String DIR = "apatch";//更新檔檔案夾
/**
* patch manager
*/
private PatchManager mPatchManager;
@Override
public void onCreate() {
super.onCreate();
// initialize
mPatchManager = new PatchManager(this);
mPatchManager.init("1.0");
Log.d(TAG, "inited.");
// load patch
mPatchManager.loadPatch();
// Log.d(TAG, "apatch loaded.");
// add patch at runtime
try {
// .apatch file path
String patchFileString = Environment.getExternalStorageDirectory()
.getAbsolutePath() + APATCH_PATH;
mPatchManager.addPatch(patchFileString);
Log.d(TAG, "apatch:" + patchFileString + " added.");
//這裡我加了個方法,複制加載更新檔成功後,删除sdcard的更新檔,避免每次進入程式都重新加載一次
File f = new File(this.getFilesDir(), DIR + APATCH_PATH);
if (f.exists()) {
boolean result = new File(patchFileString).delete();
if (!result)
Log.e(TAG, patchFileString + " delete fail");
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
public class MainActivity extends Activity {
private static final String TAG = "euler";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
toast();
}
@Override
protected void onDestroy() {
super.onDestroy();
android.os.Process.killProcess(android.os.Process.myPid());
}
//舊方法,1.apk
private void toast() { Toast.makeText(this, "old", Toast.LENGTH_SHORT).show(); }}
剛剛說的直接在gradle裡導入andfix會有個問題,是在原來的項目中,加載一次更新檔後,out.apatch檔案會copy到getFilesDir目錄下的/apatch檔案夾中,在下次更新檔更新時,會檢測更新檔是否已經添加在apatch檔案夾下,已存在就不會複制加載sdcard的out.apatch。
原來的addpath方法
public void addPatch(String path) throws IOException {
File src = new File(path);
File dest = new File(mPatchDir, src.getName());
if(!src.exists()){
throw new FileNotFoundException(path);
}
if (dest.exists()) {
Log.d(TAG, "patch [" + path + "] has be loaded.");
return;
}
FileUtil.copyFile(src, dest);// copy to patch's directory
Patch patch = addPatch(dest);
if (patch != null) {
loadPatch(patch);
}
}
修改後,判斷apatch下的out.apatch存在即删除掉,重新複制加載sdcard下的out.apatch
public void addPatch(String path) throws IOException {
File src = new File(path);
File dest = new File(mPatchDir, src.getName());
if (!src.exists()) {
throw new FileNotFoundException(path);
}
if (dest.exists()) {
Log.d(TAG, "patch [" + src.getName() + "] has be loaded.");
boolean deleteResult = dest.delete();
if (deleteResult)
Log.e(TAG, "patch [" + dest.getPath() + "] has be delete.");
else {
Log.e(TAG, "patch [" + dest.getPath() + "] delete error");
return;
}
}
FileUtil.copyFile(src, dest);// copy to patch's directory
Patch patch = addPatch(dest);
if (patch != null) {
loadPatch(patch);
}
}
還有源碼混淆
-optimizationpasses 5 # 指定代碼的壓縮級别
-dontusemixedcaseclassnames # 是否使用大小寫混合
-dontskipnonpubliclibraryclasses # 是否混淆第三方jar
-dontpreverify # 混淆時是否做預校驗
-verbose # 混淆時是否記錄日志
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆時所采用的算法
#重要,別忘了這些,不混淆andfix包,不混淆native方法
-dontwarn android.annotation
-dontwarn com.alipay.euler.**
-keep class com.alipay.euler.** {*;}
-keep class * extends java.lang.annotation.Annotation
-keepclasseswithmembernames class * {
native <methods>;
}
接着打包1.apk,
然後修改mainactivity的toast(),打包2.apk
//修改Toast內容,2.apk
private void toast() {
Toast.makeText(this, "new", Toast.LENGTH_SHORT).show();
}
cmd輸入指令,具體參數看usage
如無錯誤,編譯後會生成一個apatch檔案,改名成out.apatch
安裝打開1.apk
關閉app,将out.apatch放sdcard根目錄後,重新打開app,toast方法改變了
ps:
1. 這裡隻是簡單的測試了一下,沒有複雜的功能,而且andfix不支援布局資源等的修改。
2. github首頁的issues反應說Android4.0.4,以及5.0以上版本會crash,不過我使用了兩個虛拟機,genymotion的4.2和5.0虛拟機,沒有錯誤,其他請自測
3. 使用了apk加強時(360加強,百度加強等等),發現在加強前要先apkpatch制作更新檔,不能使用加強後的apk制作,否則更新檔無法使用,但是在加強前制作的更新檔可以很容易的被反編譯出源碼
demo下載下傳 http://download.csdn.net/detail/ityangjun/9177377