1. 程式人生 > >Android-增量更新

Android-增量更新

前言

在我們平常開發app時候,版本1.0迭代到2.0的時候目前還有不少開發得應用是全量更新,也就是重新下載整個2.0的安裝包,重新安裝到手機,其實是有些浪費資源,使用者體驗也不是很好,雖然現在是無限流量,一個app老是出現讓使用者更新全量包,極其影響使用者體驗,而增量更新可以解決2.0-1.0的差分包的大小,使用者只需要在old.apk上更新new.apk的差分包,合成新的apk即可,更新資料包會大大降低,相對上也提高了使用者體驗,獲得手機root全新還可以不知不覺讓app更新安裝完成,這種體驗相對較高,今天就來了解一下如果做到android-apk的增量更新技術。

增量更新流程

增量更新分三部分:1、安裝手機當前應用old.apk

                              2、利用工具bsdiff 去生成 old.apk 和 new.apk 之間的差分包

                              3、從伺服器下載生成的差分包到本地sd卡,和當前手機安裝的old.apk進行合併安裝即可

正文

實現增量更新,現在有各種合併差分包的開源庫,比如:bsdiff、hdiff,我們只需要獲得原始碼直接使用即可。

bsdiff下載地址:

http://www.daemonology.net/bsdiff/

bsdiff依賴bizp2(zip壓縮庫)

http://nchc.dl.sourcefore.net/project/gnuwin32/bzip2/1.0.5/bzip2-1.0.5-src.zip  

翻牆下載,下載解壓完是這樣的:

最後我會提供提供一下window下的工具(bsdiff-v4.3-win-x64)

把下載好的bspatch.c檔案 放置到cpp目錄下,放入之後會有很多報錯資訊,首先將CMakeLists.txt的新增bsdiff

add_library新增
package com.note.zlgx;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.File;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = findViewById(R.id.tv_version);
        textView.setText(BuildConfig.VERSION_NAME);
        Button button = findViewById(R.id.sample_text);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //1.下載new.apk
                new AsyncTask<Void,Void,File>(){

                    @Override
                    protected File doInBackground(Void... voids) {
                        String old = getApplication().getApplicationInfo().sourceDir;
                        bspatch(old,"/sdcard/patch","/sdcard/bestChoice/new.apk");
                        return new File("sdcard/bestChoice/new.apk");
                    }

                    @Override
                    protected void onPostExecute(File file) {
                            Intent intent = new Intent(Intent.ACTION_VIEW);

                            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
                                intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
                                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            } else {

                                // 第二個引數,即第一步中配置的authorities
                                String packageName = getApplication().getPackageName();
                                Uri contentUri = FileProvider.getUriForFile(MainActivity.this, "com.note.zlgx.fileProvider", file);
                                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                                intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
                                // 宣告需要的臨時許可權
                            }
                            startActivity(intent);
                        }

                }.execute();
                //2.合併安裝
            }
        });

    }



    /**
     *
     * @param oldapk  當前執行apk
     * @param patch    差分包檔案
     * @param output   合併之後的新apk輸入路徑
     */
    native void bspatch(String oldapk,String patch,String output);
}

bspatch 定義JNI方法,呼叫so庫方法,進行合併patch喝old.apk查分包

JNI方法區程式碼

extern "C"
JNIEXPORT void JNICALL
Java_com_note_zlgx_MainActivity_bspatch(JNIEnv *env, jobject instance, jstring oldapk_,
                                        jstring patch_, jstring output_) {
    const char *oldapk = env->GetStringUTFChars(oldapk_, 0);
    const char *patch = env->GetStringUTFChars(patch_, 0);
    const char *output = env->GetStringUTFChars(output_, 0);

    char * argv[4] = {"", const_cast<char *>(oldapk), const_cast<char *>(output),
                      const_cast<char*>(patch)};
    main(4,argv);
    // TODO

    env->ReleaseStringUTFChars(oldapk_, oldapk);
    env->ReleaseStringUTFChars(patch_, patch);
    env->ReleaseStringUTFChars(output_, output);
}