Android高級開發進階之路4——增量更新(bsdiff,bspatch,bzip,ndk)

目錄

 

增量更新

介紹

效果

步驟

總結


增量更新

最近換了個新手機,號稱2019年的android機皇一加7plus。面對這90z的屏幕,原本不玩遊戲的我也入了王者農藥的坑!不過很少玩,所以基本上每次玩都要下載補丁更新才能玩。今天我們就來講一下Android應用的增量更新。

介紹

優點:

       節省用戶更新新版本的流量、時間、內存空間。

基本流程如下:

  1. app端開發人員打包新版本上傳到服務器,new_version.apk
  2. 服務器通過bsdiff(依賴bzip2)兩個c庫,與原來純在服務器上面的舊版本進行差分,生成補丁包patch
  3. app下載補丁包patch,與手機上安裝的舊版本old_version.apk合成新的apk
  4. 安裝新的apk

 

效果

 

步驟

1、準備bsdiff和bzip2(生成補丁,合成新版本必須用到的工具)

mac/linux系統下:

下載bsdiff庫:http://www.daemonology.net/bsdiff/

由上圖可知,bsdiff庫和bspatch依賴bzip庫,所以我們還要下載一個bzip庫https://sourceforge.net/projects/bzip2/

//待更新

window系統下:

已經給大家編譯好,包括bsdiff,bspatch,bzip2,大家直接下載就好

https://download.csdn.net/download/u014736095/11869005

2、配置Android項目ndk環境

這裏不詳細展開來說,提供一個比較便捷的方法,新創建一個ndk的項目,把一下重要文件複製進來即可!

module的build.gradle文件

//...
android {
   //...
    defaultConfig {
        //...
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}

 

3、引入項目bspatch.c和bzip2的庫

如下圖,引入bspatch.c庫,用於將舊版本apk與補丁patch合成新版本apk文件。

bspatch.c所依賴的bzip2庫也要複製進來。

4、c庫代碼(至關重要)

修改CMakeLists.txt配置文件


# 設置構建本機庫所需的CMake最低版本。
cmake_minimum_required(VERSION 3.4.1)

# 聲明一個文件夾所有的c文件
file(GLOB bzip2_resource bzip2-1.0.6/*.c)

add_library( # 設置庫的名稱。
        native-lib

        #將庫設置爲共享庫。
        SHARED

        # 提供源文件的相對路徑。
        native-lib.cpp
        bspatch.c
        ${bzip2_resource}
        )

# 引入一個目錄
include_directories(bzip2-1.0.6)


find_library(
        log-lib
        log )

target_link_libraries(
        native-lib
        ${log-lib} )

重新編譯可能無法通過,會有異常,作以下修改即可

重新編譯即可。

Activity中聲明一個native函數

 /**
     * 合成新的apk安裝包
     * @param oldApk 舊版本
     * @param patch 補丁
     * @param newApk 新版本
     * @return
     */
    public native String increaseUpdate(String oldApk,String patch,String newApk);

編輯native-lib.cpp,聲明函數與之關聯,注意這個函數的命名是有規律的,Java_{類名路徑,分割用“_”}_{方法名}

5、編寫更新的代碼

涉及到文件讀寫,需要配置申請權限,併兼容高版本

AndroidManifest.xml

<manifest >
    <!--存儲-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!--位未知來源權限,適配8.0-->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
    <application
      >
        <!--四大組件之一 內容提供者 可以跨進程使用,7.0安裝必須使用它-->
        <provider
            android:authorities="com.bluetree.myapplication.fileprovider"
            android:name="android.support.v4.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">

            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>
    </application>

</manifest>

 

file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<resource xmlns:android="http://schemas.android.com/apk/res/android">
    <paths>
        <external-path name="haah" path="." />
    </paths>
</resource>

合成新版本apk,

new AsyncTask<Void, Void, File>() {
                    @Override
                    protected File doInBackground(Void... voids) {
                        createNewFile(newApkPath);
                        increaseUpdate(oldApkPath, patchPath, newApkPath);

                        return new File(newApkPath);
                    }

                    @Override
                    protected void onPostExecute(File file) {
                        super.onPostExecute(file);
                        if(file == null) return;
                        installApk(file);
                    }
                }.execute();
/**
     * 創建一個文件
     * @param path
     * @return
     */
    private File createNewFile(String path) {
        File file = new File(path);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return file;
    }

    /**
     * 安裝應用
     * @param file
     */
    private void installApk(File file) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        //兼容7.0
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        } else {
            // 聲明需要的臨時權限
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            // 第二個參數,即第一步中配置的authorities
            String packageName = getApplication().getPackageName();
            Uri contentUri = FileProvider.getUriForFile(MainActivity.this, packageName + ".fileprovider", file);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        }
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }

6、製作差分包

製作兩個版本,必須用相同的證書,否者不成功

unix下:bsdiff/. {舊版本} {新版本} {補丁名字}

 

win下命令:bsdiff {舊版本} {新版本} {補丁名字}

7、測試

標準情況下,補丁包的是在服務器上生成的,手機客戶端從服務器上下載補丁patch,與手機上存在的舊版本,合成新的版本(通過調用bspatch.c中的main方法進行合成),然後安裝!所以合成後,手機硬盤上會產生新的安裝包。這個版本實際上與之前打包出來的新版本大小是一致的。

  1. 手機上安裝app_old.apk
  2. 把補丁patch複製到手機儲存根目錄下(模擬從服務器下載補丁)
  3. 點擊“update”(增量更新)

 

 

注意事項:

1、mac或者Linux系統中,需要在bsdff文件夾中進行make指令編譯

2、bspatch.c文件記需要重新導入bzlib.h

3、正確編寫CMakeList文件

4、記得調用bspatch.c中的main()方法所傳進去的數組元素順序

 

 

 

總結

實際上增量更新的核心就是兩部分,

  • 會使用工具將新版本與舊版本進行差分,生成補丁包
  • 會配置項目的NDK環境,調用bspatch.c庫中的代碼,將下載的補丁包+舊版本合併成新版本。

 

建議先下載完整的項目下來體驗一下,然後看着源碼一步一步學習!否則很多坑,最好能夠下載這篇文章裏的庫,導入你的項目,這樣可以省去折騰因爲版本差異導致的問題!

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章