C#熱更新方案的選擇

前項(xiàng)目的C#熱更方案

小甜甜的C#熱更方案

前段時(shí)間 noodle 說他把 小甜甜 項(xiàng)目中他做的 C#熱更方案 開源了眨猎。

這個(gè)方案是一個(gè) 騷操作,不過是針對(duì) il2cpp 的织狐,核心思想是更新 libil2cpp.so轻黑,具體細(xì)節(jié)可以參考 github主頁维苔。

暗黑血統(tǒng)的C#熱更方案

再早一點(diǎn)的項(xiàng)目 暗黑血統(tǒng)论笔,那還是 Unity4 的時(shí)代采郎,我們的C#熱更方案也是一個(gè) 騷操作,這里列一下要點(diǎn):

  1. Assembly-CSharp-firstpass.dllAssembly-CSharp.dll 是Unity預(yù)定的2個(gè)程序集狂魔。

  2. Assembly-CSharp-firstpass.dll 包括了加載熱更代碼的代碼,不可被熱更新淫痰,Assembly-CSharp.dll 包括主要的游戲邏輯代碼最楷,期望可以被熱更新。

  3. 打包的時(shí)候,把 Assembly-CSharp.dll 中的代碼移動(dòng)到我們自定的 GameLogic.dll 中籽孙,并把 Assembly-CSharp.dll 清空(namespace顛倒)烈评。

  4. 運(yùn)行的時(shí)候,Assembly-CSharp-firstpass.dll 中的代碼通過 Assembly.Load 的方式去加載 GameLogic.dll犯建,GameLogic.dll 可以從服務(wù)器下載獲取讲冠,以此達(dá)到熱更新的目的。

這樣做看起來OK适瓦,但是有一個(gè)很大的限制: 預(yù)設(shè)不能掛載非firstpass目錄的腳本竿开,原因可以參考這篇帖子。 當(dāng)然玻熙,我們可以在運(yùn)行時(shí)通過 AddComponent 的方式去掛載腳本否彩,但是這樣做限制較大。

騷操作 之所以被稱為 騷操作嗦随,就是我們可以打破這個(gè)限制:即把 Assembly-CSharp.dll 換成了 GameLogic.dll 后列荔,也要保證預(yù)設(shè)能夠找得到原先引用的腳本。

暗黑血統(tǒng) 的做法是:在生成GameLogic.dll后枚尼,改cs文件對(duì)應(yīng)的meta文件贴浙,把dll重新定向到GameLogic.dll,重啟編輯器再打包署恍。

screenshot1.png

在打包的時(shí)刻悬而,預(yù)設(shè)已經(jīng)認(rèn)定了 GameLogic.dll,所以加載時(shí)就不會(huì)丟失腳本了锭汛。

當(dāng)然笨奠,這個(gè)方案依然也有局限:

  1. 必須嚴(yán)格保證 Assembly-CSharp-firstpass.dll 的穩(wěn)定,一旦出現(xiàn)了問題唤殴,只能換包般婆。

  2. 熱更后,如果 GameLogic.dll 新增了一個(gè)上架包中并不存在的腳本朵逝,那么掛載這個(gè)腳本的預(yù)設(shè)在加載時(shí)依然還是會(huì)出現(xiàn)腳本丟失蔚袍。

總體來說,這套方案沒什么大問題配名,在線上運(yùn)行良好啤咽。出現(xiàn)上面的問題2時(shí),我們就 AddComponent 繞一下渠脉。

隨著Unity的升級(jí)換代宇整,metadata的格式也在變化,這套依賴 改meta文件 的方案在版本兼容性上出現(xiàn)了很大問題芋膘,最終因?yàn)殡y以維護(hù)被拋棄了鳞青。


目前的C#熱更新方案

時(shí)至今日霸饲,如果再做方案選擇,我傾向于集成 xLua臂拓。

對(duì)于Android平臺(tái)的C#熱更厚脉,我傾向于目前公司所采用的方案:自己編譯libmono.so

我們可以在github上找到各個(gè)Unity版本對(duì)應(yīng)的 mono源碼胶惰,做如下操作:

  • 打開 image.c 文件傻工。
  • 找到 mono_image_open_from_data_with_name 函數(shù)。
  • 截住加載 Assembly-CSharp-firstpass.dll 的邏輯孵滞,做我們自己的操作中捆。

代碼流程如下:

MonoImage *
mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)
{   
    int datasize = 0; 

    if(name != NULL && strstr(name,"Assembly-CSharp-firstpass.dll"))
    {
        // 從我們的Patch目錄讀取我們自己的dll文件,覆蓋傳入的data  
    }

    // 解密data

    // 走原先的do_mono_image_load流程
}

這里有幾個(gè)細(xì)節(jié)要注意:

  1. Assembly-CSharp-firstpass.dllAssembly-CSharp.dll 我們都做了加密剃斧,所以這里有一個(gè)步驟是解密轨香。

  2. mono_image_open_from_data_with_name 函數(shù)只攔截了加載 Assembly-CSharp-firstpass.dll 的操作,并未攔截 Assembly-CSharp.dll幼东。

  3. 至于 Assembly-CSharp.dll臂容,我們打包的時(shí)候直接把他刪了,所以這里不會(huì)加載它根蟹。和 暗黑血統(tǒng) 的做法類似脓杉,我們還是通過 Assembly-CSharp-firstpass.dll 中的代碼來加載它。

改完源碼简逮,重新編譯生成新的 libmono.so球散,就大功告成了。

這個(gè)方案比較成熟散庶,網(wǎng)上的文章一搜一大把蕉堰。我之所以傾向于這個(gè)方案,主要有以下幾點(diǎn)考慮:

  1. 這個(gè)方案對(duì)整個(gè)項(xiàng)目的 侵入性很小悲龟,只需要替換掉 libmono.so 即可屋讶。

  2. 加載熱更代碼的代碼從 Assembly-CSharp-firstpass.dll 轉(zhuǎn)移到了 libmono.so,因此 Assembly-CSharp-firstpass.dll 也可以被熱更新了须教。

  3. 這個(gè)方案對(duì)團(tuán)隊(duì)人員的要求沒那么高皿渗,維護(hù)成本相對(duì)較低。

當(dāng)然轻腺,這個(gè)方案也有以下一些限制:

  1. 如果更新 Assembly-CSharp-firstpass.dll乐疆,需要重啟一次進(jìn)程。

  2. Standard Assets 或者 Plugins 目錄下的代碼可以被掛載贬养,但是 非firstpass目錄 下的代碼不行挤土,因?yàn)檫@里并沒有 暗黑血統(tǒng)改meta 的那一步騷操作。

重啟進(jìn)程對(duì)用戶體驗(yàn)有一點(diǎn)傷害煤蚌,特別是 進(jìn)程不能被快速拉起 時(shí)耕挨,可能會(huì)影響留存细卧。不過后來我們?cè)趕dk里加了一個(gè) 秒啟 的函數(shù)尉桩,現(xiàn)在重啟的代價(jià)可以忽略不計(jì)了筒占,代碼如下:

public void doRestartApp()
{
    new Thread()
    {
        public void run()
        {
            Intent localIntent = mContext.getPackageManager().getLaunchIntentForPackage(mContext.getPackageName());
            localIntent.addFlags(67108864);
            mContext.startActivity(localIntent);
            android.os.Process.killProcess(android.os.Process.myPid());
        }
    }.start();

    finish();
}

至于 非firstpass目錄 下代碼無法掛載的問題,我們會(huì)把需要掛載的代碼統(tǒng)一移動(dòng)到 Plugins 目錄下蜘犁,因?yàn)?Assembly-CSharp-firstpass.dll 已經(jīng)可以被熱更新了翰苫。


個(gè)人主頁

本文的個(gè)人主頁鏈接:https://baddogzz.github.io/2019/12/26/CSharp-Patch/

好了,拜拜这橙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奏窑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子屈扎,更是在濱河造成了極大的恐慌埃唯,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鹰晨,死亡現(xiàn)場(chǎng)離奇詭異墨叛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)模蜡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門漠趁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人忍疾,你說我怎么就攤上這事闯传。” “怎么了卤妒?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵甥绿,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我则披,道長(zhǎng)共缕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任收叶,我火速辦了婚禮骄呼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘判没。我一直安慰自己蜓萄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布澄峰。 她就那樣靜靜地躺著嫉沽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俏竞。 梳的紋絲不亂的頭發(fā)上绸硕,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天堂竟,我揣著相機(jī)與錄音,去河邊找鬼玻佩。 笑死出嘹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咬崔。 我是一名探鬼主播税稼,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼垮斯!你這毒婦竟也來了郎仆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤兜蠕,失蹤者是張志新(化名)和其女友劉穎扰肌,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熊杨,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡曙旭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了猴凹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夷狰。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖郊霎,靈堂內(nèi)的尸體忽然破棺而出沼头,到底是詐尸還是另有隱情,我是刑警寧澤书劝,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布进倍,位于F島的核電站,受9級(jí)特大地震影響购对,放射性物質(zhì)發(fā)生泄漏猾昆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一骡苞、第九天 我趴在偏房一處隱蔽的房頂上張望垂蜗。 院中可真熱鬧,春花似錦解幽、人聲如沸贴见。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽片部。三九已至,卻和暖如春霜定,著一層夾襖步出監(jiān)牢的瞬間档悠,已是汗流浹背廊鸥。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辖所,地道東北人惰说。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像奴烙,于是被迫代替她去往敵國(guó)和親助被。 傳聞我的和親對(duì)象是個(gè)殘疾皇子剖张,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354