天天看點

andfix 增量更新更新 熱更新檔修複

最近想着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 增量更新更新 熱更新檔修複
andfix 增量更新更新 熱更新檔修複
andfix 增量更新更新 熱更新檔修複
andfix 增量更新更新 熱更新檔修複

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

andfix 增量更新更新 熱更新檔修複
andfix 增量更新更新 熱更新檔修複

如無錯誤,編譯後會生成一個apatch檔案,改名成out.apatch

andfix 增量更新更新 熱更新檔修複
andfix 增量更新更新 熱更新檔修複

安裝打開1.apk

andfix 增量更新更新 熱更新檔修複
andfix 增量更新更新 熱更新檔修複

關閉app,将out.apatch放sdcard根目錄後,重新打開app,toast方法改變了

andfix 增量更新更新 熱更新檔修複
andfix 增量更新更新 熱更新檔修複

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