基于華佗熱更新(HybridCLR)的熱更新方案設(shè)計和接入

前言:主要記錄了在接入華佗熱更新中的一些思考和對整個項目的設(shè)計墓律,當(dāng)然也包括接入過程中的一些坑膀估。希望可以給需要接入熱更新的人一些啟發(fā),同時也歡迎大家留下自己的的建議和想法耻讽。

一. 什么是HybridCLR察纯?

HybridCLR(原華佗熱更新) 是一個 特性完整、零成本针肥、高性能饼记、低內(nèi)存 的近乎完美的Unity全平臺原生c#熱更方案。

沒有人比自己更了解自己的代碼慰枕,所以想了解更詳細(xì)的內(nèi)容具则,可見官方鏈接:
https://focus-creative-games.github.io/hybridclr/about/

二. 為什么使用HybridCLR?

  • 使用HybridCLR熱更新捺僻,可以以最小的成本接入項目乡洼,無需對項目進(jìn)行大規(guī)模的重寫和修改崇裁。
  • HybridCLR熱更新針對il2cpp進(jìn)行了拓展,可以直接使用C#語言開發(fā)束昵,無需使用lua拔稳,typescrpt等腳本語言。
  • AOT代碼和JIT代碼使用C#同一套語言锹雏,并且對于跨域繼承巴比,反射等高級語言特性支持完善,也無需使用多個項目進(jìn)行熱更代碼的邏輯分離礁遵,對于開發(fā)的便捷性有著很大的提升轻绞。
  • 在執(zhí)行效率和內(nèi)存占用等性能指標(biāo)上也有著大幅的的優(yōu)勢。

所以為什么使用HybridCLR佣耐?這里可以用它的創(chuàng)造者walon的自負(fù)且自信的一句話回答:
HybridCLR是一個劃時代的Unity平臺C#原生熱更新技術(shù)政勃,它將國內(nèi)Unity開發(fā)的技術(shù)框架水平提高到新的高度,并深刻地改變Unity平臺的開發(fā)生態(tài)兼砖。

三. 如何接入HybridCLR奸远?

關(guān)于如何接入HybridCLR,官方文檔已經(jīng)很詳盡了讽挟,所以具體的接入操作可以去上面官方文檔中查看懒叛。這里只介紹下接入過程中的一些問題,具體的操作不再贅述耽梅。

推薦使用unity2020.3.33版本薛窥,其他版本可能有些問題或者額外操作,等熟悉后再嘗試接入其他版本的unity眼姐。
接入流程主要分為以下步驟:

  1. 安裝 hybridclr_unity package诅迷,見官方文檔: https://focus-creative-games.github.io/hybridclr/install/
  2. 配置PlayerSettings中的設(shè)置妥凳,見官方文檔 https://focus-creative-games.github.io/hybridclr/project_settings/ 竟贯;
  3. 根據(jù)需求劃分程序集,關(guān)于如何劃分程序集的更多的內(nèi)容參考下面的章節(jié): 六.程序集設(shè)計逝钥;
  4. 配置HybridCLR的參數(shù)屑那,新手主要關(guān)注 hotUpdate Assembly Definitions (需要熱更的程序集,根據(jù)步驟3中的劃分填寫) 和 hotUpdate dlls(需要熱更的dll) 兩個參數(shù)艘款;
  5. 到這里持际,不涉及到寫代碼部分的操作已經(jīng)完成,接下來的內(nèi)容就于代碼相關(guān)了哗咆。

四. 熱更新的流程設(shè)計

在寫熱更新代碼前蜘欲,先來梳理下熱更新的流程吧。


圖1. 熱更新初始化流程

步驟 0~2

如上圖中所示晌柬,第0~2步是常規(guī)的下載-加載資源流程姥份,這些部分根據(jù)項目隨意發(fā)揮郭脂,當(dāng)然我們的熱更dll也是在這里需要打包成ab包下載下來的。

步驟 3 加載熱更dll

加載熱更的dll很簡單澈歉,只需要加載資源展鸡,然后調(diào)用Assembly.Load() 即可。

// 加載資源 (示范代碼)
var dllBytes = await AssetComponent.LoadAsync<TextAsset>("Assets/Dlls/game.bytes");
// 加載程序集
Assembly gameAss = System.Reflection.Assembly.Load(dllBytes.bytes);

步驟 4 加載補(bǔ)充元數(shù)據(jù)dll

加載補(bǔ)充元數(shù)據(jù)dll埃难。這一步是可選的莹弊,但是如果熱更程序集引用到了aot程序集的情況下,這一步是必不可少的涡尘,不然很大幾率我們會看到:

MissingMethodException: AOT generic method isn't instantiated in aot module xxx

上面這個錯誤就是因為AOT泛型函數(shù)實例化缺失導(dǎo)致的忍弛,所以我們最好補(bǔ)充元數(shù)據(jù)。補(bǔ)充元數(shù)據(jù)的代碼如下:

// 前三個是系統(tǒng)自帶考抄,推薦帶上细疚,第四個是自己游戲AOT的程序集
var AotAssemblyDlls = new string[] {
    "mscorlib","System","System.Core",
    "MyGame.Framwork",
};
for (int i = 0; i < AotAssemblyDlls.Length; i++)
{
    var aotDllName = AotAssemblyDlls[i];
    var path = $"Assets/ResBundles_Dll/aotDll/{aotDllName}.bytes";
    var asset = AssetComponent.Load<TextAsset>($"Assets/AotDlls/{aotDllName}.bytes");
    // 加載assembly對應(yīng)的dll,會自動為它hook座泳。一旦aot泛型函數(shù)的native函數(shù)不存在惠昔,用解釋器版本代碼
    LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(asset.bytes);
    if (err == LoadImageErrorCode.OK)
        Debug.Log($"{stateID}: LoadMetadataForAOTAssembly:{aotDllName}. ret:{err}");
    else
        Debug.LogError($"{stateID}: LoadMetadataForAOTAssembly:{aotDllName}. ret:{err}");
}

到這里可能很多人疑問幕与,我們的元數(shù)據(jù)dll從哪來的挑势?

其實在打包build過程中,會生成裁剪過的dll啦鸣,HybirdCLR會自動幫我們把dll拷貝到目錄:項目目錄/HybridCLRData/AssembliesPostIl2CppStrip/目標(biāo)平臺/xxx 潮饱。

我們也可以通過以下代碼獲取當(dāng)前目標(biāo)平臺的目錄:

SettingsUtil.GetAssembliesPostIl2CppStripDir(EditorUserBuildSettings.activeBuildTarget);

所以如果通過ab加載,在打包assetbundle資源前诫给,我們必須要進(jìn)行一次構(gòu)建操作香拉。然后把構(gòu)建生成的需要補(bǔ)充的dll拷貝到項目目錄中,然后根據(jù)自己的ab打包策略中狂,將dll打包成ab資源凫碌。

步驟 5 加載熱更場景,執(zhí)行熱更代碼

因為AOT代碼無法直接調(diào)用熱更代碼胃榕,那么如何運(yùn)行熱更dll里的啟動代碼呢盛险?推薦以下為比較好的方案:

推薦方案:在所有資源都已下載,所有熱更代碼都已加載后勋又,通過加載熱更資源中的場景苦掘,場景上掛有熱更腳本(例如InitAfterHotFix.cs),在InitAfterHotFix的Awake方法中進(jìn)行后續(xù)游戲的初始化楔壤。

其他方案:當(dāng)然也可以通過Assembly類獲取到熱更程序集的啟動類的啟動方法鹤啡,使用反射的方式實現(xiàn)。

五. 打包流程

在以上步驟已處理完畢蹲嚣,代碼添加好了以后递瑰,下面將開始打包的流程祟牲。
打包流程主要分成以下幾步:

  1. 編譯熱更新Dll
    unity中HybridCLR->CompileDll->ActiveBuildTarget按鈕,或者直接調(diào)用代碼: CompileDllCommand.CompileDll(EditorUserBuildSettings.activeBuildTarget);

  2. 把編譯出來的熱更Dll拷貝到項目里抖部,按照自己的ab打包策略放入自己規(guī)劃的目錄內(nèi)疲眷;

  3. 生成LinkXml,MothedBridge等等

    unity中HybridCLR->Generate->All按鈕您朽,或者直接調(diào)用代碼: HybridCLR.Editor.Commands.PrebuildCommand.GenerateAll();

  4. 拷貝AOT補(bǔ)充元數(shù)據(jù)dll到項目里狂丝,按照自己的ab打包策略放入自己規(guī)劃的目錄內(nèi);

    補(bǔ)充元數(shù)據(jù)dll的需要經(jīng)過一次打包后生成哗总,具體生成邏輯和文件目錄在第四節(jié)中有介紹几颜。

  5. 打包AssetBundle資源;

  6. 根據(jù)自己的需求讯屈,將ab資源放入StreamingAssets或遠(yuǎn)程服務(wù)器蛋哭。

六. 程序集設(shè)計

程序集設(shè)計雖然放在最后一章節(jié),但是應(yīng)該是項目最開始就優(yōu)先考慮和設(shè)計的涮母。

6.1 劃分程序集谆趾,不僅僅是為了熱更新

程序集的劃分,不僅僅為了熱更新叛本。在不需要熱更新的項目中沪蓬,合理的規(guī)劃和拆分代碼模塊,設(shè)置合理的引用關(guān)系来候,可以解除基礎(chǔ)框架-游戲模塊-三方插件的耦合跷叉。


圖2. 普通的項目的程序集劃分

如上圖中游戲模塊作為需要經(jīng)常改動的模塊,它引用了基礎(chǔ)框架模塊和三方插件营搅。而后者沒有對其的引用云挟。

如果按上圖劃分程序集并設(shè)置引用關(guān)系:代碼的依賴關(guān)系會因為程序集依賴被強(qiáng)制限制。如果某個不規(guī)范的程序員擅自在基礎(chǔ)框架中添加了涉及到游戲模塊的代碼转质,那么Ta會發(fā)現(xiàn)代碼報錯园欣,編輯器無法在基礎(chǔ)框架程序集中查到對游戲模塊程序集的代碼的引用。

如果基礎(chǔ)框架和游戲模塊同在同一個程序集中:代碼的依賴關(guān)系必須要靠個人的代碼能力管理休蟹。作為管理者必須要嚴(yán)格規(guī)范下屬并經(jīng)常review代碼沸枯,不然不規(guī)范的下屬幾天就可能把基礎(chǔ)框架和游戲模塊揉合成一團(tuán)小貓玩過的毛線團(tuán),很難理順鸡挠!

6.2 程序集與熱更新

在熱更新需求下的程序集劃分與普通的項目差別很小辉饱,基本通用,主要在于我們?nèi)绾蝿澐諥OT程序集和JIT程序集拣展,而且還有對于unity默認(rèn)的Assembly-CSharp程序集的定義彭沼。

例如上面的6.1中圖片中的例子,我們可以有2種劃分方案

  1. AOT程序集包括基礎(chǔ)框架模塊和三方插件备埃,熱更程序集包括游戲模塊的1~N個程序集姓惑。這種方案下適用于基礎(chǔ)框架穩(wěn)固完善的項目褐奴,因為基礎(chǔ)框架不會有大的變動所以不需要頻繁更新整包;

  2. AOT程序集僅包括三方插件(甚至三方插件也可以不包括)于毙,剩余所有的程序集作為熱更敦冬。這種方案下基本所有內(nèi)容都可以熱更,適用于新項目的快速迭代唯沮。

但是劃分方案2中脖旱,有個問題:

  • 下載/加載ab資源等操作,是在需要熱更的基礎(chǔ)框架中介蛉;
  • 加載熱更資源和加載dll等初始化操作萌庆,是在AOT程序集中,且會用到下載/加載ab資源代碼币旧;

所以在上面的幾條前提下践险,我們發(fā)現(xiàn)這種劃分方案下:AOT程序集中有很多對熱更程序集中代碼的調(diào)用
我們可以通過Assembly加載吹菱,通過反射調(diào)用到熱更的代碼巍虫,但是這樣必然很繁瑣。還有另外一種方法鳍刷,為了避免AOT引用熱更代碼占遥,我們也可以在AOT中實現(xiàn)另外一套資源下載/加載邏輯。當(dāng)然這2種方法都很麻煩倾剿,最好還是基礎(chǔ)框架的代碼(至少下載/加載ab資源的代碼)設(shè)置為AOT程序集筷频。

6.3 Assembly-CSharp程序集

在上述2種方案中,我們都要考慮一個特殊的程序集:unity默認(rèn)的Assembly-CSharp程序集前痘。
如果自己添加的代碼,如果沒有專門劃分程序集担忧,那么就會被設(shè)置為默認(rèn)的Assembly-CSharpAssembly-CSharp-Editor程序集芹缔。

默認(rèn)程序集的特殊之處

不確定性和混沌性:自定義的程序集必定包含在某個專屬的文件夾下或dll中,但是默認(rèn)程序集的代碼遍布整個項目目錄瓶盛,藏于各個犄角旮旯之中最欠,不便管理和統(tǒng)計。
擁有最大訪問權(quán)力:自定義的程序集需要設(shè)置對外部程序集的引用后惩猫,才可以在內(nèi)部使用相關(guān)的接口代碼芝硬。但是默認(rèn)程序集可以訪問項目內(nèi)所有的程序集(在勾選Auto Referenced的時候),它相當(dāng)于是處于程序集引用關(guān)系的金字塔頂部轧房。

熱更中默認(rèn)程序集是否熱更拌阴?

以下兩種方案都可以,但是各有利弊:

  • 我們可以將其定義為AOT程序集奶镶,不引用任何熱更程序集迟赃,那么下載熱更資源陪拘,加載dll等操作均在這里實現(xiàn)。
  • 我們也可以將其定義為熱更程序集纤壁,那么下載熱更資源左刽,加載dll等操作均在劃分的其他AOT程序集中實現(xiàn)。

把Assembly-CSharp程序集當(dāng)作熱更程序集有個不好的地方酌媒,那便是我們隨意加入的測試代碼(沒有放在有程序集定義的文件夾下)欠痴,某些導(dǎo)入的三方插件等等,只要未定義過程序集秒咨,那么默認(rèn)就會加入到Assembly-CSharp程序集斋否。所以會經(jīng)常誤把某些代碼打入熱更dll。為避免程序花費(fèi)時間和精力去檢查這些內(nèi)容拭荤,不建議用這種方案茵臭。

6.4 unity里的Assembly Definition文件

在一個普通的C#項目中,我們可以在vs中右鍵程序集(項目)條目然后打開屬性舅世,查看該程序集的相關(guān)設(shè)定旦委。但是unity生成的項目中,我們是無法使用這個操作的雏亚。

因為程序集的生成和劃分是由我們在unity中處理缨硝,然后unity自動生成。其中unity中用來劃分程序集的文件就是Assembly Definition罢低。

我們通過Assembly Definition文件查辩,可以處理以下事情:

  1. 添加自定義的宏:Define Constraints;
  2. 添加刪除程序集的依賴:Assembly Definition Refercences;
  3. 選擇生效的平臺:Platforms;

除了上面這些常用的功能,還有General選項下這些很有用的選項:

  1. Allow ‘unsafe’ Code:是否啟用不安全的代碼网持;
  2. Auto Referenced:是否自動被依賴宜岛;勾選后會被默認(rèn)的Assembly-CSharp程序集自動依賴。所以如果我們想在Assembly-CSharp中隔離對當(dāng)前程序集的依賴功舀,取消勾選萍倡。
  3. No Engine References:不依賴于引擎提供的代碼模塊。適用于可以在unity或其他平臺的項目中通用的程序集辟汰。
  4. Override References:可以手動指定所依賴的預(yù)編譯的程序集列敲,因為unity項目中的預(yù)編譯程序集可以被其他默認(rèn)依賴,勾選后當(dāng)前程序集可以選擇(不)依賴某個預(yù)編譯的程序集帖汞。
  5. Root Namespace: 當(dāng)前程序集的默認(rèn)命名空間戴而,填寫后我們使用unity添加新代碼文件,會自動添加命名空間翩蘸。我測試只有在unity中創(chuàng)建才生效所意。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扁眯,更是在濱河造成了極大的恐慌壮莹,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姻檀,死亡現(xiàn)場離奇詭異命满,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)绣版,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門胶台,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杂抽,你說我怎么就攤上這事诈唬。” “怎么了缩麸?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵铸磅,是天一觀的道長。 經(jīng)常有香客問我杭朱,道長阅仔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任弧械,我火速辦了婚禮八酒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刃唐。我一直安慰自己羞迷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布画饥。 她就那樣靜靜地躺著衔瓮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荒澡。 梳的紋絲不亂的頭發(fā)上报辱,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音单山,去河邊找鬼。 笑死幅疼,一個胖子當(dāng)著我的面吹牛米奸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爽篷,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼悴晰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起铡溪,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤漂辐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后棕硫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體髓涯,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年哈扮,在試婚紗的時候發(fā)現(xiàn)自己被綠了纬纪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡滑肉,死狀恐怖包各,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情靶庙,我是刑警寧澤问畅,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站六荒,受9級特大地震影響护姆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恬吕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一签则、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧铐料,春花似錦渐裂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至篓跛,卻和暖如春膝捞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背愧沟。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工蔬咬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沐寺。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓林艘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親混坞。 傳聞我的和親對象是個殘疾皇子狐援,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容