HybridCLR+Addressable资源框架(二)

上一篇介绍了HybridCLR的用法,这一篇讲一下通过Addressable 来下载热更dll 以及需要补充的元数据dll ,我们知道unity 是无法直接识别dll 文件,需要把那些dll 加上.byte 后缀,所以需要写个工具转换一下,首选我在Asset 下面新建HotUpdateDlls 文件夹,然后再分别建HotUpdateDll(存放热更dll 加byte后缀)MetaDataDll(存放元数据Aot 的dll 加byte后缀)
在这里插入图片描述
1、话不多说,直接上工具代码

static string HotUpdateDllPath => $"{Application.dataPath}/../HybridCLRData/HotUpdateDlls/{EditorUserBuildSettings.activeBuildTarget}/";

static string HotUpdateDestinationPath => $"{Application.dataPath}/HotUpdateDlls/HotUpdateDll/";

static string MetaDataDLLPath => $"{Application.dataPath}/../HybridCLRData/AssembliesPostIl2CppStrip/{EditorUserBuildSettings.activeBuildTarget}/";

static string MetaDataDestinationPath => $"{Application.dataPath}/HotUpdateDlls/MetaDataDll/";

static string AOTGenericReferencesPath => $"{Application.dataPath}/HybridCLRGenerate/AOTGenericReferences.cs";

[MenuItem("Build/第一次GenerateAll拷贝热更dll以及元数据dll")]
private static void HybridCLRCopyDll()
{
    // 是否有安装HybridCLR
    var controller = new InstallerController();
    if (!controller.HasInstalledHybridCLR()) 
    {
       Debug.LogError("HybridCLR is not Installer");
        return;
    }
    //执行HybridCLR
    PrebuildCommand.GenerateAll();
    CopyHotUpdateDll();
    CopyMetaDataDll();
}
[MenuItem("Build/CompileDllActiveBuildTarget拷贝以及热更dll")]
private static void HybridCLRHofixCopyDll()
{
    // 是否有安装HybridCLR
    var controller = new InstallerController();
    if (!controller.HasInstalledHybridCLR())
    {
        Debug.LogError("HybridCLR is not Installer");
        return;
    }
    CompileDllCommand.CompileDllActiveBuildTarget();
    CopyHotUpdateDll();
}


private static void CopyHotUpdateDll()
{
    var assemblies = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved;
    var dir = new DirectoryInfo(HotUpdateDllPath);
    var files = dir.GetFiles();
    var destDir = HotUpdateDestinationPath;
    if (Directory.Exists(destDir))
        Directory.Delete(destDir, true);
    Directory.CreateDirectory(destDir);
    foreach (var file in files)
    {
        if (file.Extension == ".dll" && assemblies.Contains(file.Name.Substring(0, file.Name.Length - 4)))
        {
            var desPath = destDir + file.Name + ".bytes";
            file.CopyTo(desPath, true);
        }
    }

    AssetDatabase.SaveAssets();
    AssetDatabase.Refresh();
    Debug.Log("copy hot update dlls success!");
}
private static void CopyMetaDataDll()
{
    List<string> assemblies = GetMetaDataDllList();
    var dir = new DirectoryInfo(MetaDataDLLPath);
    var files = dir.GetFiles();
    var destDir = MetaDataDestinationPath;
    if (Directory.Exists(destDir))
        Directory.Delete(destDir, true);
    Directory.CreateDirectory(destDir);
    foreach (var file in files)
    {
        if (file.Extension == ".dll" && assemblies.Contains(file.Name))
        {
            var desPath = destDir + file.Name + ".bytes";
            file.CopyTo(desPath, true);
        }
    }
    AssetDatabase.SaveAssets();
    AssetDatabase.Refresh();
    Debug.Log("copy meta data dll success!");
}
private static List<string> GetMetaDataDllList()
{
    var aotGenericRefPath = AOTGenericReferencesPath;
    List<string> result = new List<string>();
    using (StreamReader reader = new StreamReader(aotGenericRefPath))
    {
        var lineStr = "";
        while (!reader.ReadLine().Contains("new List<string>"))
        {
        }

        reader.ReadLine();
        while (true)
        {
            lineStr = reader.ReadLine().Replace("\t", "");
            if (lineStr.Contains("};"))
                break;
            var dllName = lineStr.Substring(1, lineStr.Length - 3);
            result.Add(dllName);
        }
    }

    return result;
}              

这个工具干的工作 就是把生成的dll 放到指定的文件夹下 ,要说一下,元数据dll 其实是读的HybridCLRGenerate/AOTGenericReferences 里面的PatchedAOTAssemblyList
2,首先建两个场景,一个是 GameLaunher 启动场景,属于Aot(在Scenes In Build 勾选上),另一个是GameStart 场景,GameStart 是热更新完成后首个加载的热更新场景,只需要把热更脚本 挂载在这个场景上就行了 ,而且还不需要任何反射操作,也是官方推荐的做法
在这里插入图片描述
3、来看一下 Addressable 的分组,直接把所对应的文件夹拖上去就行了
在这里插入图片描述
4、先建一个热更程序集,看下图
在这里插入图片描述
5、首先在GameLaunher 新建一个进度条 ,然后挂上Launher 启动脚本
在这里插入图片描述
直接看Launher 脚本

 private byte[] _dllBytes;
 public Slider slider;
 void Start()
 {
     StartCoroutine(GameLaunch());
 }

 private IEnumerator GameLaunch() 
 {
     yield return DoUpdateAddressadble();   // 加载ab 
     yield return LoadDll();
     yield return EnterGame();
 }
 IEnumerator DoUpdateAddressadble()
 {
     AsyncOperationHandle<IResourceLocator> initHandle = Addressables.InitializeAsync();
     yield return initHandle;

     // 检测更新
     var checkHandle = Addressables.CheckForCatalogUpdates(false);
     yield return checkHandle;
     if (checkHandle.Status != AsyncOperationStatus.Succeeded)
     {
         Debug.LogError($"CheckForCatalogUpdates Error{checkHandle.OperationException.ToString()}");
         yield break;
     }
     if (checkHandle.Result.Count > 0)
     {
         var updateHandle = Addressables.UpdateCatalogs(checkHandle.Result, false);
         yield return updateHandle;

         if (updateHandle.Status != AsyncOperationStatus.Succeeded)
         {
             Debug.LogError($"UpdateCatalogs Error{updateHandle.OperationException.ToString()}");
             yield break;
         }

         // 更新列表迭代器
         List<IResourceLocator> locators = updateHandle.Result;
         foreach (var locator in locators)
         {
             List<object> keys = new List<object>();
             foreach (var key in locator.Keys)
             {
                 if (key is string)
                 {
                     keys.Add(key);
                 }
             }
             var sizeHandle = Addressables.GetDownloadSizeAsync(keys);
             yield return sizeHandle;
             if (sizeHandle.Status != AsyncOperationStatus.Succeeded)
             {
                 Debug.LogError($"GetDownloadSizeAsync Error{sizeHandle.OperationException.ToString()}");
                 yield break;
             }

             long totalDownloadSize = sizeHandle.Result;
             Debug.Log("download size : " + totalDownloadSize);
             if (totalDownloadSize > 0)
             {
                 // 下载
                 var downloadHandle = Addressables.DownloadDependenciesAsync(keys, Addressables.MergeMode.Union);
                 while (!downloadHandle.IsDone)
                 {
                     if (downloadHandle.Status == AsyncOperationStatus.Failed)
                     {
                         Debug.LogError($"DownloadDependenciesAsync Error{downloadHandle.OperationException.ToString()}");
                         yield break;
                     }
                     // 下载进度
                     float percentage = downloadHandle.PercentComplete;
                     Debug.Log($"已下载: {percentage}");
                     slider.value = percentage;
                     yield return null;
                 }
                 if (downloadHandle.Status == AsyncOperationStatus.Succeeded)
                 {
                     Debug.Log("下载完毕!");
                     slider.value = 1f;
                 }
             }
             else
             {
                 Debug.Log("无下载");
                 slider.value = 1f;
             }
         }
     }
     else
     {
         Debug.Log("没有检测到更新!");
         slider.value = 1f;
     }

     // 进游戏
 }
 private IEnumerator LoadDll()
 {
     yield return LoadMetadataForAOTAssemblies();
     yield return LoadGameHotUpdateDll();
     yield return ReloadAddressableCatalog();
     Debug.Log("LoadAssemblies finish!");
     yield return null;
 }
 private IEnumerator LoadGameHotUpdateDll()
 {
     var path = $"Assets/HotUpdateDlls/HotUpdateDll/GameHotUpdate.dll.bytes";
     ReadDllBytes(path);
     if (_dllBytes != null)
     {
         var assembly = Assembly.Load(_dllBytes);
         Debug.Log($"Load Assembly success,assembly Name:{assembly.FullName}");
     }

     yield return null;
 }
 //补充元数据
 private IEnumerator LoadMetadataForAOTAssemblies()
 {
     List<string> aotDllList = new List<string>
         {
             "mscorlib.dll",
             //"System.dll",
             //"System.Core.dll", // 如果使用了Linq,需要这个
         };

     foreach (var aotDllName in aotDllList)
     {
         var path = $"Assets/HotUpdateDlls/MetaDataDll/{aotDllName}.bytes";
         ReadDllBytes(path);
         if (_dllBytes != null)
         {
             var err = HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(_dllBytes, HomologousImageMode.SuperSet);
             Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. ret:{err}");
         }
     }
     yield return null;

     Debug.Log("LoadMetadataForAOTAssemblies finish!");
 }
 private void ReadDllBytes(string path)
 {
     var dllText = LoadAsset<TextAsset>(path);

     if (dllText == null)
     {
         Debug.LogError($"cant load dllText,path:{path}");
         _dllBytes = null;
     }
     else
     {
         _dllBytes = dllText.bytes;
     }

     UnloadAsset(dllText);
 }

 #region 为了方便 所以写到这里了 
 public T LoadAsset<T>(string path)
 {
     var op = Addressables.LoadAssetAsync<T>(path);
     if (!op.IsValid())
         return default;
     op.WaitForCompletion();
     return op.Result;
 }
 public IEnumerator LoadScene(string sceneName)
 {
     var op = Addressables.LoadSceneAsync(sceneName, LoadSceneMode.Single);
     yield return op;
     if (op.Status != AsyncOperationStatus.Succeeded)
     {
         Debug.LogError(
             $"load scene failed,exception:{op.OperationException.Message} \r\n {op.OperationException.StackTrace}");
     }
 }
 public void UnloadAsset(UnityEngine.Object asset)
 {
     if (asset != null)
         Addressables.Release(asset);
 }
 // 加载完dll 要  重新加载catalog
 private IEnumerator ReloadAddressableCatalog()
 {
     var op = Addressables.LoadContentCatalogAsync($"{Addressables.RuntimePath}/catalog.json");
     yield return op;
     if (op.Status != AsyncOperationStatus.Succeeded)
     {
         Debug.LogError(
             $"load content catalog failed, exception:{op.OperationException.Message} \r\n {op.OperationException.StackTrace}");
     }
 }
 private IEnumerator EnterGame()
 {
     yield return LoadScene("Assets/Scenes/GameScenes/GameStart.unity");
     Debug.Log("EnterGame");
 }
 #endregion

注意:ReloadAddressableCatalog 这个方法 在加载完dll 跑一下,请看官网所提到的bug
在这里插入图片描述
如果想看更详细的,请看这位大佬写的博客 ok,剩下的 就按照Addressable 的打包资源流程走就行了 ,我这边 安卓和IOS 都跑一遍没问题,有啥不对的,欢迎小伙伴来指正哈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值