Unity Android il2cpp的完美熱更解決方案

1. 簡介

這是Unity Android il2cpp的完美熱更解決方案的Demo(Git地址)的說明。android

和現有的熱更解決方案不一樣的是,他不會引入多餘的語言(只是UnityScript,c#...),對Unity程序設計和編碼沒有任何限制。你能夠在預置和場景裏的GameObject上添加任何的Compnents組件,須要序列化的和不須要序列化的,他們都是能夠熱更的,也不須要作額外的標記處理。簡而言之,在此方案下,Unity的全部資源和腳本,都是能夠熱更的。git

本文接下來將介紹如何去製做熱更文件和如何應用這些熱更文件。爲了簡化Demo的設計,Demo包含的熱更文件會事先以全量更新的方式製做好,一塊兒打到了Apk裏面。具體到項目中熱更文件得放服務器,正式上線得放CDN,以增量更新的方式搗鼓出和文中同樣的目錄結構就OK了。github

2. 方案總覽

Unity在以il2cpp方式導出Android工程(或者Apk文件)的時候,代碼會被編譯成libil2cpp,而相關的資源、配置和序列化數據會以他們各自的格式導出到android的assets目錄(assets/bin/Data)。這兩部分,libil2cpp和assets目錄,必須匹配(即須要在同一次打包中提取,可能有的變了,有的沒變,增量方式只提取變化的部分)才能正常工做,否則Unity會在啓動時崩潰。本方案就是熱更這兩部分。bootstrap

熱更的正式流程以下圖.c#

運行時流程圖

流程說明:windows

  • 步驟1,在Unity的邏輯以前,libbootstrap會檢查本地是否有Patch. Apk安裝後,沒應用過任何熱更,本地是不會有Patch文件的,走no流程。若是熱更過,則會有Patch目錄,走yes流程。Patch目錄如何準備,後面會將到。bash

  • 步驟2,加載Patch目錄對應架構(arm/x86)的libil2cpp庫,並應用assets目錄的更新文件。服務器

  • 步驟3,開始Unity的流程,進入Unity第一個場景,並執行相關的Unity Script,通常是C#,咱們都以C#舉例。架構

  • 步驟4,檢查服務端是否有新的patch,這步demo沒有演示,須要本身實現。app

  • 步驟5,下載新的patch,這步demo也沒有演示,須要本身實現

  • 步驟6,根據規則準備patch目錄,詳細規則會在後面描述。在Demo中只是將全量更新包解壓,全量更新包打包的時候目錄結構就是對的,因此不須要作其餘的處理。

  • 步驟7,調用libbootstrap的接口設置patch目錄,由於libil2cpp已經加載進進程,因此須要重啓APP,重新的patch目錄加載patch。這步Demo中有設置patch目錄的例子。

  • 步驟8,重啓APP,Demo提供了純C#代碼。

  • 步驟9,沒有新的Patch,就正常進入遊戲了。

流程裏更詳細的描述和如何生成Patch文件,見第三章。

3. Demo詳述

3.1. Demo目錄結構

工程全部文件均置於AndroidIl2cppPatchDemo目錄下。各文件目錄說明以下表。

名字 說明
Editor/AndroidBuilder.cs 這個文件包含全部從導出Android工程,到輸出Patch和生成Apk安裝文件的代碼。
Editor/Exe/zip.exe zip壓縮工具,用來將asset/bin/Data下的文件壓縮成標準zip格式。
Plugin/ 包含libbootstrap庫.
PrebuiltPatches/ 包含預先生成的兩個全量熱更新版本。
Scene/*.unity 演示場景,母包和版本1僅有0.unity,版本2增長了1.unity,測試新增場景和腳本的patch
Script/Bootstrap.cs 這個文件定義了libbootstrap的c#接口和重啓APP的純c#實現
Script/VersionSettor.cs 這個腳本用於運行時準備相應的熱更版本目錄。
Script/UI/MessageBoxUI.cs 這是一個簡單的運行時MessageBox控制器。
ZipLibrary/ c#版的壓縮解壓工具,輸出的zip文件爲非標準文件,Patch製做中不能用於asset/bin/Data文件的壓縮,僅用於libil2cpp庫的壓縮,運行時用於全量熱更包的解壓.

全部文件就這麼多,項目用git管理,master分支爲母包分支,version1和version2分支爲熱更1和熱更2分支,分支間會有些細微的差異,version1主要測試序列化數據,version2添加了新場景和新腳本,具體能夠diff查看。下面會詳細描述打包過程和如何應用熱更文件。

3.2. 打包過程

全部的打包邏輯在文件Editor\AndroidBuilder.cs裏。展開主菜單AndroidBuilder, 能夠看到有5步,爲了和熱更啓動流程區分,咱們就叫他過程。

  • 過程1:以il2cpp的方式,導出Gradle Android工程。選擇Gradle Android工程,而不是ADT Android工程,只是由於Unity2018再也不支持ADT方式。Demo並不依賴AndroidStudio,只是導出的Android工程目錄結構是以Gradle的方式註釋,以後的構建步驟都是調用原始JDK/SDK的方式。Demo這部分的代碼能夠複用,但須要根據項目需求作一些修改。
  • 過程2:須要修改一下Android工程,由於libbootstrap須要在進入Unity的幀循環前,檢查加載本地準備好的patch。大多數狀況,你能夠複用這個步驟的代碼。可是若是你的項目修改了Unity Java的繼承體系,你須要檢查一下這塊代碼是否有調用到。若是沒有調用到,後面Unity幀循環中的邏輯和資源,用的都是Apk內的相應文件。

  • 過程3:生成熱更文件。如在第二章所述,patch分爲兩部分,il2cpp庫和assets/bin/Data目錄。具體作法代碼均有提供,須要注意的是必須遵照各個文件的命名方式和相對路徑。各個文件均有壓縮,對於增量包,若是壓縮前的文件和以前相比沒有變化,則不須要製做對應的壓縮文件。這部分製做壓縮部分的代碼可複用,增量部分須要本身實現,熱更文件最好也加進版本管理(svn/git/...)中。

  • 過程4: 生成打包的windows腳本。腳本僅依賴JDK/SDK命令,可複用。生成腳本後,Android工程就不依賴Unity了,能夠隨意替換文件,再次調用腳本生成新的Apk。須要注意的是,打包用的so動態庫,是pkg_raw目錄下的so文件,替換時請注意。首次會在Unity目錄下生成keystore目錄和相應的簽名文件,能夠將此簽名替換,並修改導出腳本中的簽名密碼。

  • 過程5: 執行過程4中的腳本,生成Apk安裝文件,可複用。

主菜單AndroidBuilder下還提供了菜單「Running Step 1, 2, 4, 5 for the base version」,這是一鍵構建母包版本用的,母包不須要製做patch文件,因此少了過程3;和菜單「Runnnig Step 1-4 for patch versions」,這是一鍵構建Patch用的,由於在demo裏,不須要導出Apk文件。

關於打包這裏得多說兩句。 若是沒有采用AssetBundle的方式打包,Unity會按各自格式,將全部場景和依賴輸出到assets/bin/Data目錄,這樣子也是能夠熱更的。可是,不要這麼作,由於這樣作微小的改動會影響到多個文件,致使熱更文件過大。最好是本身用AssetBundle的方式將資源作一個清晰的劃分,打包好的AssetBundle放在assets下的其餘目錄。須要注意和libil2cpp庫和assets/bin/Data的文件向匹配(保證是同一個版本的輸出)。運行時能夠重寫AssetBundleManager.overrideBaseDownloadingURL加載最新的AssetBundle。

3.3. 運行時應用熱更文件

咱們回顧一下第二章的流程圖,結合打包過程和Demo的代碼,作進一步的說明.

運行時流程圖

打包過程2裏,在Unity的遊戲邏輯以前,插入了三行Java代碼。

+        System.loadLibrary("main");
+        System.loadLibrary("unity");
+        System.loadLibrary("bootstrap");
        mUnityPlayer = new UnityPlayer(this);
複製代碼

這三行代碼保證了上圖中步驟1-2能在步驟3以前執行,下一行mUnityPlayer的代碼即開始了步驟3的執行。步驟3以後全部的邏輯,都是已熱更過的il2cpp庫裏的Unity Script(c#,...)了。熱更部分的邏輯若是有修改,會在熱更後體現,若是這部分的bug不影響下次熱更,則能夠經過熱更修復,不然應指引用戶清除本地數據,以母包熱更邏輯更新到最新。因此,在方案的應用中,仍需儘可能保證熱更部分的代碼穩定,不能隨意更改。

如前所述,Demo裏沒有步驟4和步驟5的相關邏輯,步驟6中Patch的準備,Demo只是簡單地將全量壓縮包解壓,相關邏輯在Script/VersionSettor.cs文件中。準備更新目錄時,應保證libil2cpp部分被解壓,命名方式和Demo保持一致,而assets_bin_Data下的文件不須要解壓,應保證目錄結構和Demo保持一致。若是是增量更新,Patch目錄下的文件應該是相對於母包的修改文件。在持續熱更中,應保證在步驟7前,本地當前Patch目錄的完整性(保證運行中的App還能正常執行),新的Patch應新建目錄,經過硬連接的形式從當前Patch目錄中提取所須要的沒變化的文件,準備好後執行步驟7,重啓後將老Patch目錄刪除. 步驟7和步驟8的代碼也在Script/VersionSettor.cs文件中,樣子以下

//4. tell libboostrap.so to use the right patch after reboot
        string error = Bootstrap.use_data_dir(runtimePatchPath);
        if (!string.IsNullOrEmpty(error))
        {
            messageBox.Show("use failed. path:" + zipLibil2cppPath + ", error:" + error, "ok", () => { messageBox.Close(); });
            yield break;
        }

        //5. reboot app
        yield return StartCoroutine(Restart());
複製代碼

4. Verify 和 Build

4.1. Verify

安裝預編譯的Apk文件,點擊按鈕能夠切換各個版本。

Apk連接

4.2. Build

依賴

  • Unity (我用Unity2017/Unity2018)
  • JDK.(我用JDK1.8)
  • Android SDK.(我用Android SDK platform 23 和 build tools 26.0.2(低於26.0.2,Unity2018不支持),最好是這兩個版本,否則得從新下載)
  • Android NDK r13b. (Unity il2cpp原來只支持這個版本,如今不知道放開沒,反正這個版本沒問題)
  • Git

Build指引

    1. 在Unity中(Edit->Preference->External tools)設置好 JDK/SDK/NDK 路徑,打包代碼裏會從Unity中讀取。
    1. 若是你的Android SDK的build tools版本不是26.0.2,須要修改代碼 AndroidBuilder.cs,第14行.
    1. 若是你使用的Android platform不是android-23,修改代碼 AndroidBuilder.cs,第15行.
    1. 出母包,執行菜單 AndroidBuilder->Run Step 1, 2, 4, 5 for base version, 成功後會彈出文件管理器顯示apk所在的目錄.
    1. 通常來講你不須要打Patch文件,若是要打,用git checkout version1或version2,執行菜單 AndroidBuilder->Run Step 1-4 for Patch Version。PrebuildPatches目錄下的相應文件會被更新。

5. 剩下的工做和建議

打包部分

  • 設置部分須要根據項目實際作修改。
  • 熱更文件的增量版本化管理。

運行時部分

  • 檢查新版本和下載熱更文件。
  • 持續增量更新的Patch目錄的準備。
  • 用Asset Bundle管理資源。

另外,打包的工做盡可能自動的一鍵化,一次化,除非你想在打包當晚集體曬月亮。另外,低成本的打包流程,你們都願意在真機上看結果,利於產品的穩定。Demo其實提供了一套自動化的框架和腳本,理解透,化爲己用,也是幸事一件。若是有更好的方式,歡迎討論。

相關文章
相關標籤/搜索