【原生】CocosCreator 原生 熱更新(demo源碼、動態熱更、強更新)


版本:2.4.2

 

參考:

cocos教程:熱更新范例教程

cocos教程:熱更新管理器

csdn:Cocos Creator 熱更新(動態修改熱更地址)

 

demo下載:

hotUpdateDemo

 

這里用cocos2.4.2版本,從零實現android熱更新,從1.0.0版本熱更到2.0.0版本的demo操作流程。從而了解熱更新的基本環境搭建和原理。

 

一 看官方教程

二 從零新建項目,實現android熱更新

三 版本從1.0.0熱更新到2.0.0

四 遇到的問題

五 動態熱更

六 強更新

一 看官方教程

首先瀏覽下官方的教程熱更新范例教程熱更新管理器。知道熱更新大概的原理流程。

更新流程大致如下:

  1. 基於原生打包目錄中的 assets 和 src 目錄生成本地 Manifest 文件。
  2. 創建一個熱更新組件來負責熱更新邏輯。
  3. 游戲發布后,若需要更新版本,則生成一套遠程版本資源,包含 assets 目錄、src 目錄和 Manifest 文件,將遠程版本部署到服務端。
  4. 當熱更新組件檢測到服務端 Manifest 版本不一致時,就會開始熱更新

 

本地構建發布android文件和熱更新遠程文件如下:

 

assets和src:代碼和圖片等資源

project.manifest:版本配置文件

version.manifest:project.manifest的一部分,因為project.manifest保存了資源配置導致過大,每次加載影響體驗,所以抽離了遠程資源地址和版本號到version.manifest中,以方便快速加載比對版本。

 

project.manifiest文件:

 version.manifest

 

 

二 從零新建項目,實現android熱更新

1.  搭建原生開發環境,新建空項目,並構建-編譯-運行android。

 參考:cocos原生開發環境配置

 

2. 下載官網Demo

下載地址:https://github.com/cocos-creator/tutorial-hot-update

下載下來就有了疑問,demo是什么年代的版本?都幾年幾個月沒更新了,要配置什么環境怎么樣才能跑起來?要用到當下新版本里要怎么修改?

api過時了怎么辦?跑起來各種報錯怎么整?我用的TS但是demo是JS怎么辦?

所以demo我就不跑了,新建一個項目跑。

 

3. 復制熱更新插件

新建一個TS項目后,復制demo里的packages到新項目中相同位置。packages里是熱更新插件,會在構建項目時,在main.js里插入一段熱更新相關代碼。

 

4. 本地搭建遠程資源目錄

在新項目中創建remote-assets文件夾,用於存放遠程版本資源。

 

安裝python2.7.5+版本,我用的是2.7.13,在新項目中創建一個python_server.bat文件

 

文件內容如下:

python -m SimpleHTTPServer 8000

 

雙擊bat,相當於以新項目為根目錄搭建了一個簡易服務器。

 

瀏覽器輸入如下,則相當於訪問新項目遠程資源remote-assets文件夾 (192.168.0.60替換成你電腦本地IP)

http://192.168.0.60:8000/remote-assets/

 

 

5. 復制並修改熱更新組件

從demo中找到HotUpdate.js,cocos提供的是js的,里面是熱更新UI和邏輯的代碼,我新項目用的是ts。

把demo的HotUpdate.js的代碼復制到新項目的HotUpdate.ts里,並做相應的修改。

 

修改1:

demo里的版本文件ManifestStr是直接寫死在HotUpdate.js里,這肯定是不行的。

 

修改將customManifestStr值從版本文件manifest獲取

 

修改2:

cc.loader已經過時,將兩處使用cc.loader轉換md5的地方都注釋掉。

 

修改3:

將demo里用到的組件UI和變量都重新寫過,新項目用的UI丑點無所謂,組件重命名了也沒關系,反正只要和官方demo的匹配上就行。

 

另外的修改可以參考Demo源碼,把HotUpdate.ts組件掛到新項目場景組件上,這樣新項目加載場景后就可以執行熱更新了。

 

6. 復制版本生成器

復制version_generator.js到新項目中相同位置,這個版本生成文件會生成當前版本的project.manifest和version.manifest配置文件。

 

新項目中新建熱更新.bat文件,這個用於執行version_generator.js。

 

熱更新.bat內容如下:

@ECHO OFF
@node version_generator.js -v 1.0.0 -u http://192.168.0.60:8000/remote-assets/ -s build/jsb-link/ -d assets/
pause

參數說明:

  • -v 指定 Manifest 文件的主版本號。
  • -u 指定服務器遠程包的地址,這個地址需要和最初發布版本中 Manifest 文件的遠程包地址一致,否則無法檢測到更新。
  • -s 本地原生打包版本的目錄相對路徑。
  • -d 保存 Manifest 文件的地址。

 

總結下新項目現在都做了什么:

1. 新建項目,並能夠正常構建-編譯-運行,在真機上可以跑起來這個空項目。

2. 下載官方熱更新demo

3. 復制了熱更新插件package

4. 本地搭建了遠程版本資源目錄remote-assets

5. 熱更新組件HotUpdate.js改成了ts

6. 復制了版本生成器version_generator.js

 

做完以上的操作,基本環境就准備好了。

 

三  版本從1.0.0熱更新到2.0.0

大致操作流程如下:

1. 構建2.0.0項目

2. 將2.0.0項目資源和版本配置放到遠程資源文件夾remote-assets

3. 構建1.0.0項目

4. 運行1.0.0項目,熱更新到2.0.0

 

1. 構建2.0.0項目

發布原生不需要勾選md5

 

2.執行熱更新.bat

將熱更新.bat的-v修改成2.0.0,雙擊執行。這是根據當前2.0.0項目,生成了2.0.0版本文件project.manifest和version.manifest,將這兩個文件復制放到resource下。

在MainScene上掛載HotUpdate.ts組件,並將project.manifest拖動賦值給組件HotUpdate的manifestUrl屬性。

 

 

3. 再次構建項目

這是將上一步生成的project.manifest和version.manifest打包進去。將project.manifest和version.manifest復制到remote-assets下

 

 將build/js-link下assets和src文件夾復制到remote-assets下

 

這樣就在remote-assets中有個2.0.0版本的熱更包

 

 

 

4. 修改場景,構建項目

這是構建1.0.0版本項目,任意修改項目場景,比如增加一個圖片,增加一個label文本等,能看出來和2.0.0不一樣。

 

5. 執行熱更新.bat

將熱更新.bat的-v修改成1.0.0,雙擊執行,這是根據項目生成1.0.0版本的project.manifest和version.manifest。

將1.0.0版本的這兩個文件覆蓋掉項目resouces下2.0.0版本的。

 

6. 構建項目

這是將上一步生成的1.0.0版本文件project.manifest和version.manifest打包進去

 

7. androd studio 真機運行項目(也可以cocos模擬器先跑跑)

真機運行時,是1.0.0版本

 

點擊檢查更新,提示有新版本

 

點擊立即更新,則更新到2.0.0版本

 

四  遇到的問題

1. 發布原生不要勾選md5 Cache

發布原生不需要勾選md5,勾選了以后熱更新會找不到文件。

例如不勾選md5時發布文件名為abc.json,勾選md5后文件名變成abc.aaa.json,會多出一串字符。但是熱更新仍然會去找abc.json,導致找不到。

 

2. 真機每次測試需要刪除一次app

運行項目時,需要刪除手機上app,這樣才能刪除原apk的版本文件緩存.

 

3.熱更能從1.0.0更新到2.0.0,能不能從2.0.0回退到1.0.0?

官方提供的版本比較函數,只有服務端版本>客戶端版本時,才會進行更新。所以不能從2.0.0回退到1.0.0版本。

    /**
     * 版本對比
     * @param versionA 客戶端版本
     * @param versionB 服務端版本
     * @returns 客戶端>服務端返回正數; 客戶端=服務端返回0; 客戶端<服務端返回負數
     */
    private versionCompareHandle (versionA, versionB) {
        console.log("[HotUpdate] JS Custom Version Compare: version A is " + versionA + ', version B is ' + versionB);
        var vA = versionA.split('.');
        var vB = versionB.split('.');
        for (var i = 0; i < vA.length; ++i) {
            var a = parseInt(vA[i]);
            var b = parseInt(vB[i] || 0);
            if (a === b) {
                continue;
            }
            else {
                return a - b;
            }
        }
        if (vB.length > vA.length) {
            return -1;
        }
        else {
            return 0;
        }
    };

 需要修改如下,只要服務端和客戶端版本號不一致,就進行更新,這樣可以進行版本回退。

    /**
     * 版本對比
     * @param versionA 客戶端版本
     * @param versionB 服務端版本
     * @returns 客戶端>服務端返回正數; 客戶端=服務端返回0; 客戶端<服務端返回負數
     */
    private versionCompareHandle (versionA, versionB) {
        console.log("[HotUpdate] JS Custom Version Compare: version A is " + versionA + ', version B is ' + versionB);
        var vA = versionA.split('.');
        var vB = versionB.split('.');
        //長度不相等,則進行更新
        if(vA.length != vB.length){
            return -1;
        }
        for (var i = 0; i < vA.length; ++i) {
            var a = parseInt(vA[i]);
            var b = parseInt(vB[i] || 0);
            //數字相同,則跳過
            if (a === b) {
                continue;
                //數字不同,則進行更新
            }else {
                return -1;
            }
        }
        //長度相等且數字相等,則不更新
        return 0;
    };

 

五 動態更新

為什么需要動態熱更地址?

 cocos的熱更新地址是放在project.manifest文件里的。如果這個地址固定的話,當游戲有新版本,需要打包測試熱更時,就必須放在這個地址下測試,而線上用戶也是這個地址,勢必會影響用戶的使用。

所以熱更的地址理想存放一個版本一個地址

game01/version1.0.0

game01/version1.0.1

game01//version2.0.0

 

需要修改哪里才能實現動態地址熱更?

熱更新的代碼位置,具體可以查看源碼

 

熱更新中manifest文件的作用如下,動態指定熱更地址需要修改游戲內resouces下manifest以及原生熱更目錄下manifest兩個地方的文件。

 

 

在HotUpdate.ts中加入修改manifest文件代碼。

當第一次熱更時,本地熱更目錄下是沒有manfiest文件的,需要創建一個並修改該文件熱更地址。

當第二次或之后熱更新,本地熱更目錄下存在manifest文件,則修改該文件熱更地址。

hotUpdateUrlCache用於判斷manifest是否已修改為最新地址,如果已修改為最新地址,則不需要再次修改。

    /**
     * 修改.manifest文件
     * @param {新的升級包地址} newAppHotUpdateUrl       http://192.168.0.119:8000/remote-assets-new/
     * @param {修改manifest文件后回調} resultCallback 
     */
    public modifyAppLoadUrlForManifestFile(newAppHotUpdateUrl, resultCallback) {
        //如果新地址和緩存地址一致,表示manifest已經被修改成最新地址,不需要再次修改manifest文件
        let hotUpdateUrlCache = cc.sys.localStorage.getItem(this.hotUpdateUrlCacheKey);
        if (hotUpdateUrlCache && hotUpdateUrlCache == newAppHotUpdateUrl) {
            resultCallback();
            return;
        }
        //本地熱更目錄下已存在project.manifest,則直接修改已存在的project.manifest
        if (jsb.fileUtils.isFileExist(this._storagePath + "/project.manifest")) {
            console.log("[HotUpdate] modifyAppLoadUrlForManifestFile: 有下載的manifest文件,直接修改熱更地址");
            //修改project.manifest
            let projectManifest = jsb.fileUtils.getStringFromFile(this._storagePath + "/project.manifest");
            let projectManifestObj = JSON.parse(projectManifest);
            projectManifestObj.packageUrl = newAppHotUpdateUrl;
            projectManifestObj.remoteManifestUrl = newAppHotUpdateUrl + 'project.manifest';
            projectManifestObj.remoteVersionUrl = newAppHotUpdateUrl + 'version.manifest';
            let afterString = JSON.stringify(projectManifestObj);
            let isWrittenProject = jsb.fileUtils.writeStringToFile(afterString, this._storagePath + "/project.manifest");
            //更新數據庫中的新請求地址,下次如果檢測到不一致就重新修改 manifest 文件
            if (isWrittenProject) {
                cc.sys.localStorage.setItem(this.hotUpdateUrlCacheKey, newAppHotUpdateUrl);
            }
            console.log("[HotUpdate] 修改是否成功,project.manifest:", isWrittenProject);
            console.log("[HotUpdate] 修改后文件:", projectManifestObj.packageUrl, projectManifestObj.remoteManifestUrl, projectManifestObj.remoteVersionUrl);
            resultCallback();
            //不存在熱更文件夾,則新建一個熱更文件夾和project.manifest
        } else {
            console.log("[HotUpdate] modifyAppLoadUrlForManifestFile: 不存在熱更文件夾,新建一個,然后修改熱更地址");
            if (!jsb.fileUtils.isDirectoryExist(this._storagePath)) jsb.fileUtils.createDirectory(this._storagePath);
            //修改原始project文件
            let projectManifest = jsb.fileUtils.getStringFromFile(this.projectManifest.nativeUrl);
            let projectManifestObj = JSON.parse(projectManifest);
            projectManifestObj.packageUrl = newAppHotUpdateUrl;
            projectManifestObj.remoteManifestUrl = newAppHotUpdateUrl + 'project.manifest';
            projectManifestObj.remoteVersionUrl = newAppHotUpdateUrl + 'version.manifest';
            let afterString = JSON.stringify(projectManifestObj);
            let isWrittenProject = jsb.fileUtils.writeStringToFile(afterString, this._storagePath + '/project.manifest');
            if (isWrittenProject) {
                cc.sys.localStorage.setItem(this.hotUpdateUrlCacheKey, newAppHotUpdateUrl);
            }
            console.log("[HotUpdate] 修改是否成功,project.manifest:", isWrittenProject);
            console.log("[HotUpdate] 修改后文件:", projectManifestObj.packageUrl, projectManifestObj.remoteManifestUrl, projectManifestObj.remoteVersionUrl);
            resultCallback();
        }
    }

  

 

 游戲開始后,先訪問后台獲取最新熱更地址,這里假設新地址是"http://192.168.0.119:8000/remote-assets/version2.0.1"。

然后調用modifyApploadUrlForManifestFile修改manifest文件的熱更地址,修改后走正常熱更流程。

        this.hotUpdate.modifyAppLoadUrlForManifestFile("http://192.168.0.119:8000/remote-assets/version2.0.1", ()=>{
            this.hotUpdate.checkUpdate();
        });

 

六 強更新

打開游戲后,從后台獲取是否需要強更新,如果需要強更新,則打開瀏覽器下載新的app。

apkUrl是apk所在地址,比如http://www.test.com/test.apk

AppActivity.java:

    /**
     * 打開瀏覽器更新下載新版本apk
     * @param apkUrl    apk地址
     */
    public static void openBrowserUpdate(String apkUrl) {
        Intent intent = new Intent();
        intent.setAction("android.intent.action.VIEW");
        Uri apk_url = Uri.parse(apkUrl);
        intent.setData(apk_url);
        AppActivity.context.startActivity(intent); //startActivity無法在靜態方法中使用,所以提前保存content
    }

 

因為startActivity不能在靜態方法中使用,所以定義了一個context變量保存當前Activity。 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM