【Unity】AB包下载

news2024/10/3 4:42:36

【Unity】AB包下载

1.使用插件打AB包

a.AB包分类

一般地,将预制体作为AB包资源,不仅需要对预制体本身进行归类,还要对其涉及的动画(AnimationClip)、动画状态机(AnimatorController)、以及所用到的骨骼(Avatar)、模型(Mesh)、模型所用的材质(Matreial)、材质所用的图片(Images)、Shader等进行归类。
还有一种是AB包依赖情况,后面接触会在这里补充。
归类的资源对象名字是自定义,这里写XXX.unity3d,是为了在项目中处理。最后打包出来的就是这个名字的AB包,不过从AB包加载出来具体预制体时,就是当前打包时改预制体的原名。例如是存放在XXX.unity3d的AB包中的名字为 Cube 预制体。
在这里插入图片描述
在这里插入图片描述

b.使用官方打包工具 AssetBundles-Browser-master

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
压缩对象的设置及输出文件
在这里插入图片描述
在这里插入图片描述

2.创建AB包对比文件

AB包对比文件的作用:

  1. 判断本地是否存在AB包,有就不需要从服务器下载
  2. 判断本地AB包是否最新,不是则需要从服务器下载

递归查找文件夹中所有文件信息

    /// <summary>
    /// 获取文件信息
    /// </summary>
    /// <param name="target">根目录文件</param>
    /// <returns>该目录所有文件信息,包括子文件夹中的</returns>
    private List<FileInfo> GetFoldersInfo(string target)
    {
        int length = 0;
        List<FileInfo> fileInfolist = new List<FileInfo>();
        //获取文件夹下所有文件
        DirectoryInfo directory = Directory.CreateDirectory(target);
        FileInfo[] fileInfos = directory.GetFiles();
        length = fileInfos.Length;
        for (int i = 0; i < length; i++)
        {
            fileInfolist.Add(fileInfos[i]);
        }
        //获取文件夹下所有文件夹
        string[] folders = Directory.GetDirectories(target);
        length = folders.Length;
        for (int i = 0; i < length; i++)
        {
            //获取子文件夹中所有文件
            List<FileInfo> lst = GetFoldersInfo(folders[i]);
            int len = lst.Count;
            for (int j = 0; j < len; j++)
            {
                fileInfolist.Add(lst[j]);
            }
        }
        return fileInfolist;
    }

获取某个文件的MD5码

    /// <summary>
    /// 获取MD5码
    /// </summary>
    /// <param name="filePath">文件路径</param>
    /// <returns>该文件的MD5码</returns>
    private string GetFileMD5(string filePath)
    {
        //打开文件
        using (FileStream file = new FileStream(filePath, FileMode.Open))
        {
            //使用MD5格式
            MD5 md5 = new MD5CryptoServiceProvider();
            //获取文件MD码
            byte[] data = md5.ComputeHash(file);
            //关闭文件
            file.Close();
            //释放占用资源
            file.Dispose();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                //将数据转化成小写的16进制
                sb.Append(data[i].ToString("x2"));
            }
            return sb.ToString();
        }
    }

创建AB包对比文件信息

    /// <summary>
    /// 创建AB包对比文件
    /// </summary>
    private void CreateABCompareFile()
    {
        文件夹信息
        //DirectoryInfo directory = Directory.CreateDirectory(Application.dataPath + "/ABResource/AB/" + platformStr[platformIdx]);
        获取文件夹中所有文件
        //FileInfo[] fileInfos = directory.GetFiles();

        //获取路径中所有的文件信息 platformStr[platformIdx]为生成AB包的路劲 例如:platformStr[platformIdx] == "Android"
        List<FileInfo> fileInfos = GetFoldersInfo(Application.dataPath + "/ABResource/AB/" + platformStr[platformIdx]);
        //声明字符串用来存储AB对比数据
        string abCompareInfo = string.Empty;
        int index = -1;
        string localPath = "";

        foreach (FileInfo fileInfo in fileInfos)
        {
            //没有扩展名的就是AB包,这里只是客观的进行判断,具体看打出来的AB包规则
            //AB包保存的目录也会生成一个文件
            if (fileInfo.Extension == "" && fileInfo.Name != platformStr[platformIdx])
            {
                //存储 AB包名字 字节长度 MD5码  用空格隔开,用"|"隔开每个文件信息
                
                //收集相对路劲
                index = fileInfo.FullName.LastIndexOf(platformStr[platformIdx]);
                if(index == -1)
                {
                    continue;
                }
                localPath = fileInfo.FullName.Substring(index + platformStr[platformIdx].Length + 1);
                localPath = localPath.Replace("\\", "/");

                //abCompareInfo += fileInfo.Name + " " + fileInfo.Length + " " + GetFileMD5(fileInfo.FullName);
                //存储成字符串
                abCompareInfo += localPath + " " + fileInfo.Length + " " + GetFileMD5(fileInfo.FullName);
                abCompareInfo += "|";
            }
        }
        //去除最后一个“|”字符
        abCompareInfo = abCompareInfo.Substring(0, abCompareInfo.Length - 1);
        //创建 ABCompareInfo.txt 文件作为AB包对比信息文件
        File.WriteAllText(Application.dataPath + "/ABResource/AB/" + platformStr[platformIdx] + "/ABCompareInfo.txt", abCompareInfo);
        //刷新Unity资源目录
        AssetDatabase.Refresh();
        Debug.Log("[" + platformStr[platformIdx] + "]生成AB包对比文件成功");
    }
调用CreateABCompareFile()后,就会在对应的文件目录下生成AB包对比文件,接着将对比文件和AB包文件上传到服务器。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.从服务器下载文件并保存本地

    //服务器路劲
    private static string serverPath = "http://127.0.0.1:8800/Game/";
    
    /// <summary>
    /// 下载文件并保存本地
    /// </summary>
    /// <param name="fileName">下载的文件路径(不包括服务器路径 包括后缀)</param>
    /// <param name="localPath">保存路径(包括文件名和后缀)</param>
    /// <param name="callback">结束回调</param>
    /// <returns>下载文件携程</returns>
    private IEnumerator DownLoadFile(string filePath, string localPath, UnityAction<bool> callback = null)
    {
        //下载的文件路劲 serverPath 为服务器所存放AB包的目录 这里使用的是本地服务器 例如:serverPath == "http://127.0.0.1:8800/Game/"
        string requestPath =
#if UNITY_EDITOR
            serverPath + "PC/" + filePath;
#else
            serverPath + "Android/" + filePath;
#endif
        UnityWebRequest unityWebRequest = UnityWebRequest.Get(requestPath);
        yield return unityWebRequest.SendWebRequest();

        //没有出现错误并且下载完毕
        if(!unityWebRequest.isHttpError && !unityWebRequest.isNetworkError && unityWebRequest.isDone)
        {
            //将文件保存到本地
            using (FileStream fileStream = File.Create(localPath))
            {
                fileStream.Write(unityWebRequest.downloadHandler.data, 0, unityWebRequest.downloadHandler.data.Length);
            }
            //回调成功
            Debug.Log("下载" + filePath + "成功");
            if (callback != null)
            {
                callback(true);
            }
        }
        else
        {
            //回调失败
            Debug.Log("下载" + filePath + "失败");
            if (callback != null)
            {
                callback(false);
            }
        }
    }

4.下载AB包对比文件

    //服务器AB包对比文件名 以及 本地AB包对比文件名
    private static string remoteABCompareName = "ABCompareInfo.txt";
    
    /// <summary>
    /// 下载AB包对比文件
    /// </summary>
    /// <param name="callback">结束回调</param>
    private void DownLoadABCompareFile(UnityAction<bool> callback = null)
    {

        //开启下载 remoteABCompareName 文件携程 remoteABCompareName为AB包对比文件名 例如:remoteABCompareName == "ABCompareInfo.txt"
        //下载完成后会保存到 数据持久化路径Application.persistentDataPath
        StartCoroutine(DownLoadFile(remoteABCompareName, Application.persistentDataPath + "/" + remoteABCompareName, isDown =>
        {
            if (isDown)
            {
                //这里代表已经下载并保存到本地完成
                if (callback != null)
                {
                    callback(true);
                }
            }
            else
            {
                if (callback != null)
                {
                    callback(false);
                }
            }
        }));
    }
至此,已经完成AB包对比文件的下载。将 [ http://127.0.0.1:8800/Game/ABCompareInfo.txt ](本地服务器的AB包对比文件)下载下来,并保存为[ Application.persistentDataPath + "/" + ABCompareInfo.txt ]。

5.失败后多次下载的优化

用户由于网络不稳定,会出现下载失败的情况,因此设计自动重新下载的次数,让用户在规定次数中,无需手动进行下载。
    //从服务器下载下来后,临时保存的对比文件路径名
    private static string localABCompareName = "ABCompareInfo_TMP.txt";
    
    //是否成功下载AB包对比文件
    private bool isSuccDownLoadABCompare = false;
    //重新下载AB包对比文件次数
    private int reDownLoadCompareMaxNums = 5;
    //当前下载AB包对比文件次数
    private int downLoadCompareNums = 0;

    /// <summary>
    /// 下载AB包对比文件
    /// </summary>
    /// <param name="callback">结束回调</param>
    private void DownLoadABCompareFile(UnityAction<bool> callback = null)
    {
        //没有成功下载,并且下载次数小于重新下载最大次数
        if (!isSuccDownLoadABCompare && downLoadCompareNums < reDownLoadCompareMaxNums)
        {
            downLoadCompareNums++;
            //开启下载 remoteABCompareName 文件携程 remoteABCompareName为AB包对比文件名 例如:localABCompareName == remoteABCompareName == "ABCompareInfo.txt"
            //下载完成后会保存到 数据持久化路径Application.persistentDataPath
            StartCoroutine(DownLoadFile(remoteABCompareName, Application.persistentDataPath + "/" + localABCompareName, isDown =>
            {
                if (isDown)
                {
                    //这里代表已经下载并保存到本地完成
                    if (callback != null)
                    {
                        callback(true);
                    }
                    isSuccDownLoadABCompare = true;
                }
                else
                {
                    Debug.Log("下载" + remoteABCompareName + "失败,重试[" + downLoadCompareNums + "]次");
                    //下载方法回调失败
                    isSuccDownLoadABCompare = false;
                    if (downLoadCompareNums < reDownLoadCompareMaxNums)
                    {
                        //还可以重新下载
                        DownLoadABCompareFile(callback);
                    }
                    else
                    {
                        //重新下载次数用尽 回调失败
                        if (callback != null)
                        {
                            callback(false);
                        }
                    }
                }
            }));
        }
    }

6.收集AB包对比文件的信息

    private class ABInfo
    {
        public string name;
        public long size;
        public string md5;
        public ABInfo(string name, string size, string md5)
        {
            this.name = name;
            this.size = long.Parse(size);
            this.md5 = md5;
        }
    }
    private Dictionary<string, ABInfo> remoteABInfo = new Dictionary<string, ABInfo>();
    
    /// <summary>
    /// AB包对比文件信息收集
    /// </summary>
    /// <param name="callback">收集结束回调</param>
    private void GetABCompareFileInfo(UnityAction<bool> callback)
    {
        if(!File.Exists(Application.persistentDataPath + "/" + remoteABCompareName))
        {
            //路劲不存在的情况下
            if (callback != null)
            {
                //回调收集信息失败
                callback(false);
                return;
            }
        }
        //读取下载的AB包对比文件路径
        string info = File.ReadAllText(Application.persistentDataPath + "/" + remoteABCompareName);
        //将数据信息拆分成每个AB包信息
        string[] strs = info.Split('|');
        string[] infos = null;
        for (int i = 0; i < strs.Length; i++)
        {
            //将每个AB包信息拆分成独立数据
            infos = strs[i].Split(' ');
            //使用 remoteABInfo 字典和 ABInfo 类存储AB包信息
            remoteABInfo.Add(infos[0], new ABInfo(infos[0], infos[1], infos[2]));
        }

        if(callback != null)
        {
            //回调收集成功
            callback(true);
        }
    }

7.下载AB包

    //AB包待下载List集合  用于存储AB包的相对路径名
    private List<string> downLoadList = new List<string>();
    /// <summary>
    /// 对比信息后 下载AB包
    /// </summary>
    /// <param name="callback">结束回调</param>
    /// <returns></returns>
    private IEnumerator DownLoadABFile(UnityAction<bool> callback = null)
    {        
        string localPath = Application.persistentDataPath + "/";
        //临时List集合:用于记录成功下载的AB文件名
        List<string> tempList = new List<string>();
        //最大允许重新下载次数
        int reDownLoadABFileMaxNums = 5;
        //下载次数
        int downLoadABFileNums = 0;
        //下载成功个数
        int downLoadOverNums = 0;
        //待下载总数
        int downLoadOverMaxNums = downLoadList.Count;
        //待下载总数大于0 并且 下载次数在允许重新在次数中
        while (downLoadList.Count > 0 && downLoadABFileNums < reDownLoadABFileMaxNums)
        {
            //下载次数增加
            downLoadABFileNums++;

            //下载集合中开始下载
            for (int i = 0; i < downLoadList.Count; i++)
            {
                //在本地创建目录供AB包保存
                string[] relativePaths = downLoadList[i].Split('/');
                string tmpDir = localPath;
                for(int j = 0; j < relativePaths.Length - 1; j++)
                {
                    tmpDir = tmpDir + relativePaths[j];
                    if (!Directory.Exists(tmpDir))
                    {
                        Directory.CreateDirectory(tmpDir);
                    }
                    tmpDir = tmpDir + "/";
                }
            
                //开启下载文件协程  downLoadList[i] 文件名     localPath + downLoadList[i] 保存地址    下载结束回调
                yield return StartCoroutine(DownLoadFile(downLoadList[i], localPath + downLoadList[i], isDown =>
                {
                    if(isDown)
                    {
                        //下载成功 记录下载的文件
                        tempList.Add(downLoadList[i]);
                        downLoadOverNums++;
                    }
                }));

                Debug.Log("下载AB包进度:" + downLoadOverNums + "/" + downLoadOverMaxNums);
            }
            //将下载成功的文件名从待下载列表删除
            for (int i = 0; i < tempList.Count; i++)
            {
                downLoadList.Remove(tempList[i]);
            }
            //清空记录的文件名List集合
            tempList.Clear();

            //如果待下载集合中还有文件没下载继续循环
        }

        if(downLoadList.Count == 0)
        {
            //待下载集合没有内容,下载所有的AB包成功
            if (callback != null)
            {
                //回调成功
                callback(true);
            }
        }
        else
        {
            //下载失败
            if (callback != null)
            {
                //回调失败
                callback(false);
            }
        }
    }
至此,已经可以根据AB包对比文件中的AB包信息下载AB包。将 remoteABInfo 中的key添加到downLoadList中,就能够完成AB包对比文件中所包含的AB包的下载。

添加到 DownLoadABFile 协程中,写在用到 downLoadList 的代码 之前。

    foreach (string name in remoteABInfo.Keys)
    {
        downLoadList.Add(name);
    }

在这里插入图片描述

8.添加待下载AB包路径名的逻辑及代码优化

待下载List集合需添加AB包路径名进行下载的逻辑:

  1. 客户端未包含任何AB包,需要下载读取到的对比文件中所有的AB包。
  2. 客户端包含部分AB包,需要下载本地对比文件中没有保存的AB包。
  3. 客户端包含部分旧的AB包,需要对比本地对比文件和服务器对比文件,从而下载最新的AB包。

根据项目不同情况,将部分AB包 及 这部分AB包的信息文件(AB包对比文件)存放在 streamingAssetsPath 中,跟随包一起导出。或者不将任何AB包跟随包一起导出。因此,从服务器下载AB包对比文件后,如果包中原本存在AB包对比文件,则需要对比两个文件,从而确定要进行下载或者更新的AB包,从而添加到待下载队列进行下载,之后记得更新本地AB包对比文件。

a.优化AB包对比文件信息,达到重用目的

    //服务器对比文件信息
    private Dictionary<string, ABInfo> remoteABInfo = new Dictionary<string, ABInfo>();
    //本地对比文件信息
    private Dictionary<string, ABInfo> localABInfo = new Dictionary<string, ABInfo>();
    /// <summary>
    /// AB包对比文件信息收集(更新)
    /// </summary>
    /// <param name="filePath">对比文件路径</param>
    /// <param name="dic">存放信息的对应字典</param>
    /// <param name="callback">收集结束回调</param>
    private void GetABCompareFileInfo(string filePath, Dictionary<string, ABInfo> dic, UnityAction<bool> callback)
    {
        if (!File.Exists(filePath))
        {
            //路劲不存在的情况下
            if (callback != null)
            {
                //回调收集信息失败
                callback(false);
                return;
            }
        }
        //读取下载的AB包对比文件路径
        string info = File.ReadAllText(filePath);
        //将数据信息拆分成每个AB包信息
        string[] strs = info.Split('|');
        string[] infos = null;
        for (int i = 0; i < strs.Length; i++)
        {
            //将每个AB包信息拆分成独立数据
            infos = strs[i].Split(' ');
            //使用字典和 ABInfo 类存储AB包信息
            dic.Add(infos[0], new ABInfo(infos[0], infos[1], infos[2]));
        }

        if (callback != null)
        {
            //回调收集成功
            callback(true);
        }
    }

b.判断并收集本地AB包对比文件信息

    /// <summary>
    /// 根据本地最新AB对比文件 收集信息
    /// </summary>
    /// <param name="callback">收集结束回调</param>
    private void GetLocalABCompareFileInfo(UnityAction<bool> callback)
    {
        if (File.Exists(Application.persistentDataPath + "/" + remoteABCompareName))
        {
            //Application.persistentDataPath + "/" + remoteABCompareName 指的是之后更新后的本地AB包对比文件
            GetABCompareFileInfo(Application.persistentDataPath + "/" + remoteABCompareName, localABInfo, callback);
        }
        else if(File.Exists(Application.streamingAssetsPath + "/" + remoteABCompareName))
        {
            //Application.streamingAssetsPath + "/" + remoteABCompareName 指的是初始跟随包一起导出的AB包对比文件
            GetABCompareFileInfo(Application.streamingAssetsPath + "/" + remoteABCompareName, localABInfo, callback);
        }
        else
        {
            //没有AB包对比文件

            //没有AB包对比文件代表没有AB包,所以本地AB包信息应当清空
            localABInfo.Clear();

            //默认成功收集本地AB包信息
            callback(true);
        }
    }

c.两AB包对比文件进行对比的逻辑

从服务器下载下来的AB包对比文件(localABCompareName)与本地存在的AB包对比文件(remoteABCompareName)进行对比。

    /// <summary>
    /// 检查是否需要更新资源
    /// </summary>
    /// <param name="overCallback">结束回调</param>
    public void CheckUpdate(UnityAction<bool> overCallback)
    {
        //清空服务器对比文件信息及本地对比文件信息的字典 及 待下载List集合
        remoteABInfo.Clear();
        localABInfo.Clear();
        downLoadList.Clear();

        //从服务器下载AB包对比文件 服务器AB包对比文件名字:remoteABCompareName  保存到本地后的AB包对比文件名字:localABCompareName
        DownLoadABCompareFile(isOver =>
        {
            if (isOver)
            {
                //下载成功

                //收集从服务器下载下来的AB包对比文件信息 并 保存到 remoteABInfo 字典中
                GetABCompareFileInfo(Application.persistentDataPath + "/" + localABCompareName, remoteABInfo, overCallback);

                //服务器AB包对比文件信息收集成功

                //获取本地AB对比文件 并 收集对比文件信息 保存到localABInfo中
                GetLocalABCompareFileInfo(isCollect =>
                {
                    if (isCollect)
                    {
                        //本地AB包对比文件信息收集成功

                        //获取本地AB包对比文件的成功回调

                        foreach (string abName in remoteABInfo.Keys)
                        {
                            if (!localABInfo.ContainsKey(abName))
                            {
                                //将本地没有的AB包路径名添加到待下载List集合中
                                downLoadList.Add(abName);
                            }
                            else
                            {
                                //本地有的AB包则需要对比两者MD5码是否一样
                                if (localABInfo[abName].md5 != remoteABInfo[abName].md5)
                                {
                                    //MD5码不一样,代表本地AB包不是最新的,需要下载,因此也添加到待下载List集合中
                                    downLoadList.Add(abName);
                                }
                                //检查后删除与服务器一样的资源信息
                                localABInfo.Remove(abName);
                            }
                        }
                        foreach (string abName in localABInfo.Keys)
                        {
                            //本地多出来的无用AB包需要进行删除
                            if (File.Exists(Application.persistentDataPath + "/" + abName))
                            {
                                File.Delete(Application.persistentDataPath + "/" + abName);
                            }
                        }

                        //两AB包对比文件对比结束 开始下载AB包
                        StartCoroutine(DownLoadABFile(isFinish =>
                        {
                            if (isFinish)
                            {
                                //完成后 更新本地AB包对比文件
                                string remoteInfo = File.ReadAllText(Application.persistentDataPath + "/" + localABCompareName);
                                File.WriteAllText(Application.persistentDataPath + "/" + remoteABCompareName, remoteInfo);
                            }
                            else
                            {
                                Debug.Log("下载AB包失败");
                            }

                            //下载AB包结束 回调结束
                            overCallback(isFinish);

                        }));
                    }
                    else
                    {
                        //收集本地AB包对比信息失败 回调失败
                        overCallback(false);
                    }
                });
            }
            else
            {
                //下载服务器AB包对比文件失败 回调失败
                overCallback(false);
            }
        });
    }
至此,整个流程基本结束。下载AB包对比文件,并与存在本地的AB包对比文件进行对比,之后下载最新和缺失的AB包。

9.优化资源更新判断逻辑,加入自定义消息回调

/// <summary>
/// 下载回调类型
/// </summary>
public enum DownCallbackType
{
    None,
    Message,
    Progress
}

    /// <summary>
    /// 检查是否需要更新资源
    /// </summary>
    /// <param name="overCallback">结束回调</param>
    public void CheckUpdate(UnityAction<bool> overCallback, UnityAction<DownCallbackType, string> updateCallback)
    {
        //清空服务器对比文件信息及本地对比文件信息的字典 及 待下载List集合
        remoteABInfo.Clear();
        localABInfo.Clear();
        downLoadList.Clear();

        updateCallback(DownCallbackType.Message, "下载的本地路径|" + Application.persistentDataPath);

        //从服务器下载AB包对比文件 服务器AB包对比文件名字:remoteABCompareName  保存到本地后的AB包对比文件名字:localABCompareName
        DownLoadABCompareFile(isOver =>
        {
            if(isOver)
            {
                //下载成功

                updateCallback(DownCallbackType.Message, "下载服务器对比文件成功");

                //收集从服务器下载下来的AB包对比文件信息 并 保存到 remoteABInfo 字典中
                GetABCompareFileInfo(Application.persistentDataPath + "/" + localABCompareName, remoteABInfo, overCallback);

                updateCallback(DownCallbackType.Message, "服务器AB包对比文件信息收集成功");
                //服务器AB包对比文件信息收集成功

                //获取本地AB对比文件 并 收集对比文件信息 保存到localABInfo中
                GetLocalABCompareFileInfo(isCollect =>
                {
                    if (isCollect)
                    {
                        //本地AB包对比文件信息收集成功

                        //获取本地AB包对比文件的成功回调
                        updateCallback(DownCallbackType.Message, "本地AB包对比文件信息收集成功");
                        
                        foreach (string abName in remoteABInfo.Keys)
                        {
                            if(!localABInfo.ContainsKey(abName))
                            {
                                //将本地没有的AB包路径名添加到待下载List集合中
                                downLoadList.Add(abName);
                            }
                            else
                            {
                                //本地有的AB包则需要对比两者MD5码是否一样
                                if (localABInfo[abName].md5 != remoteABInfo[abName].md5)
                                {
                                    //MD5码不一样,代表本地AB包不是最新的,需要下载,因此也添加到待下载List集合中
                                    downLoadList.Add(abName);
                                }
                                //检查后删除与服务器一样的资源信息
                                localABInfo.Remove(abName);
                            }
                        }

                        foreach(string abName in localABInfo.Keys)
                        {
                            //本地多出来的无用AB包需要进行删除
                            if (File.Exists(Application.persistentDataPath + "/" + abName))
                            {
                                File.Delete(Application.persistentDataPath + "/" + abName);
                            }
                        }

                        updateCallback(DownCallbackType.Message, "两AB包对比文件对比结束 开始下载AB包");

                        //两AB包对比文件对比结束 开始下载AB包
                        StartCoroutine(DownLoadABFile(isFinish =>
                        {
                            if(isFinish)
                            {
                                //完成后 更新本地AB包对比文件
                                string remoteInfo = File.ReadAllText(Application.persistentDataPath + "/" + localABCompareName);
                                File.WriteAllText(Application.persistentDataPath + "/" + remoteABCompareName, remoteInfo);

                                updateCallback(DownCallbackType.None, "AB包下载结束");
                            }
                            else
                            {
                                updateCallback(DownCallbackType.None, "AB包下载失败");
                            }

                            //下载AB包结束 回调结束
                            overCallback(isFinish);

                        }, updateCallback));
                    }
                    else
                    {
                        updateCallback(DownCallbackType.Message, "收集本地AB包对比信息失败");
                        //收集本地AB包对比信息失败 回调失败
                        overCallback(false);
                    }
                });
            }
            else
            {
                updateCallback(DownCallbackType.Message, "下载服务器AB包对比文件失败");
                //下载服务器AB包对比文件失败 回调失败
                overCallback(false);
            }
        });
    }
    
    /// <summary>
    /// 对比信息后 下载AB包
    /// </summary>
    /// <param name="callback">结束回调</param>
    /// <param name="updateCallback">消息回调</param>
    /// <returns></returns>
    private IEnumerator DownLoadABFile(UnityAction<bool> callback = null, UnityAction<DownCallbackType, string> updateCallback = null)
    {
        //foreach (string name in remoteABInfo.Keys)
        //{
        //    downLoadList.Add(name);
        //}

        string localPath = Application.persistentDataPath + "/";
        //临时List集合:用于记录成功下载的AB文件名
        List<string> tempList = new List<string>();
        //最大允许重新下载次数
        int reDownLoadABFileMaxNums = 5;
        //下载次数
        int downLoadABFileNums = 0;
        //下载成功个数
        int downLoadOverNums = 0;
        //待下载总数
        int downLoadOverMaxNums = downLoadList.Count;
        //待下载总数大于0 并且 下载次数在允许重新在次数中
        while (downLoadList.Count > 0 && downLoadABFileNums < reDownLoadABFileMaxNums)
        {
            //下载次数增加
            downLoadABFileNums++;

            //下载集合中开始下载
            for (int i = 0; i < downLoadList.Count; i++)
            {
                //在本地创建目录供AB包保存
                string[] relativePaths = downLoadList[i].Split('/');
                string tmpDir = localPath;
                for(int j = 0; j < relativePaths.Length - 1; j++)
                {
                    tmpDir = tmpDir + relativePaths[j];
                    if (!Directory.Exists(tmpDir))
                    {
                        Directory.CreateDirectory(tmpDir);
                    }
                    tmpDir = tmpDir + "/";
                }

                //开启下载文件协程  downLoadList[i] 文件名     localPath + downLoadList[i] 保存地址    下载结束回调
                yield return StartCoroutine(DownLoadFile(downLoadList[i], localPath + downLoadList[i], isDown =>
                {
                    if(isDown)
                    {
                        //下载成功 记录下载的文件
                        tempList.Add(downLoadList[i]);
                        downLoadOverNums++;
                    }
                }));

                //Debug.Log("下载AB包进度:" + downLoadOverNums + "/" + downLoadOverMaxNums);
                updateCallback(DownCallbackType.Progress, downLoadOverNums + "/" + downLoadOverMaxNums);
            }
            //将下载成功的文件名从待下载列表删除
            for (int i = 0; i < tempList.Count; i++)
            {
                downLoadList.Remove(tempList[i]);
            }
            //清空记录的文件名List集合
            tempList.Clear();

            //如果待下载集合中还有文件没下载继续循环
        }

        if(downLoadList.Count == 0)
        {
            //待下载集合没有内容,下载所有的AB包成功
            if (callback != null)
            {
                //回调成功
                callback(true);
            }
        }
        else
        {
            //下载失败
            if (callback != null)
            {
                //回调失败
                callback(false);
            }
        }
    }

10.在外部代码中调用的方法

CheckUpdate(isOver =>
{
    if (isOver)
    {
        Debug.Log("下载结束, 跳转场景");
    }
    else
    {
        Debug.Log("失败,弹出重新游戏弹框");
    }
}, (callbackType, message) =>
{
    switch (callbackType)
    {
        case DownCallbackType.Message:
            Debug.Log(message);
            break;
        case DownCallbackType.Progress:
            Debug.Log("AB包下载进度:" + message);
            break;
    }
});

因为作者精力有限,文章中难免出现一些错漏,敬请广大专家和网友批评、指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1402026.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

7、机器学习中的数据泄露(Data Leakage)

找到并修复这个以微妙的方式破坏你的模型的问题。 数据泄露这个概念在kaggle算法竞赛中经常被提到,这个不同于我们通常说的生活中隐私数据暴露,而是在竞赛中经常出现某支队伍靠着对极个别feature的充分利用,立即将对手超越,成功霸占冠军位置,而且与第二名的差距远超第二名…

[足式机器人]Part2 Dr. CAN学习笔记- 最优控制Optimal Control Ch07-1最优控制问题与性能指标

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - 最优控制Optimal Control Ch07-1最优控制问题与性能指标

基于OpenSSL的SSL/TLS加密套件全解析

概述 SSL/TLS握手时&#xff0c;客户端与服务端协商加密套件是很重要的一个步骤&#xff0c;协商出加密套件后才能继续完成后续的握手和加密通信。而现在SSL/TLS协议通信的实现&#xff0c;基本都是通过OpenSSL开源库&#xff0c;本文章就主要介绍下加密套件的含义以及如何在O…

机器人制作开源方案 | 全自动导航分拣机器人

作者&#xff1a;孙国峰 董阳 张鑫源 单位&#xff1a;山东科技大学 机械电子工程学院 指导老师&#xff1a;张永超 贝广霞 1. 研究意义 1.1 研究背景 在工业生产中&#xff0c;机器人在解决企业的劳动力不足&#xff0c;提高企业劳动生产率&#xff0c;提高产品质量和降低…

【设计模式】适配器和桥接器模式有什么区别?

今天我探讨一下适配器模式和桥接模式&#xff0c;这两种模式往往容易被混淆&#xff0c;我们希望通过比较他们的区别和联系&#xff0c;能够让大家有更清晰的认识。 适配器模式&#xff1a;连接不兼容接口 当你有一个类的接口不兼容你的系统&#xff0c;而你又不希望修改这个…

【笔记】Blender4.0建模入门-3物体的基本操作

Blender入门 ——邵发 3.1 物体的移动 演示&#xff1a; 1、选中一个物体 2、选中移动工具 3、移动 - 沿坐标轴移动 - 在坐标平面内移动 - 自由移动&#xff08;不好控制&#xff09; 选中物体&#xff1a;右上的大纲窗口&#xff0c;点击物体名称&#xff0c;物体的轮…

【Python 元编程】装饰器入门指南

Python装饰器入门指南&#x1f680; 在编程世界中&#xff0c;效率和优雅的代码往往是我们所追求的目标。Python 作为一种强大且灵活的编程语言&#xff0c;提供了一个称为“装饰器”的功能&#xff0c;让我们能够以一种简洁和优雅的方式扩展和管理我们的代码。 本文旨在为初…

利用appium自动控制移动设备并提取数据

安装appium-python-client模块并启动已安装好的环境 安装appium-python-client模块 在window的虚拟环境下执行pip install appium-python-client 启动夜神模拟器&#xff0c;进入夜神模拟器所在的安装路径的bin目录下&#xff0c;进入cmd终端&#xff0c;使用adb命令建立adb…

生产环境 OpenFeign 的配置最佳实践

基础使用 OpenFeign 全方位讲解 1. 生产环境 OpenFeign 的配置事项 1.1 如何更改 OpenFeign 默认的负载均衡策略 warehouse-service: #服务提供者的微服务IDribbon:#设置对应的负载均衡类NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule1.2 开启默认的 O…

C++ //练习 2.14 下面的程序合法吗?如果合法,它将输出什么?

C Primer&#xff08;第5版&#xff09; 练习 2.14 练习 2.14 下面的程序合法吗&#xff1f;如果合法&#xff0c;它将输出什么&#xff1f; int i 100, sum 0; for(int i 0; i ! 10; i)sum i; std::cout<<i<<" "<<sum<<std::endl;环境…

scipy通过快速傅里叶变换实现滤波

文章目录 fft模块简介fft函数示例滤波 fft模块简介 scipy官网宣称&#xff0c;fftpack模块将不再更新&#xff0c;或许不久之后将被废弃&#xff0c;也就是说fft将是唯一的傅里叶变换模块。 Fourier变换极其逆变换在数学上的定义如下 F ( ω ) ∫ − ∞ ∞ f ( t ) e − i ω…

Python图像处理【19】基于霍夫变换的目标检测

基于霍夫变换的目标检测 0. 前言1. 使用圆形霍夫变换统计图像中圆形对象2. 使用渐进概率霍夫变换检测直线2.1 渐进霍夫变换原理2.2 直线检测 3. 使用广义霍夫变换检测任意形状的对象3.1 广义霍夫变换原理3.2 检测自定义形状 小结系列链接 0. 前言 霍夫变换 (Hough Transform,…

2024最新:optee系统开发精讲 - 课程介绍

&#xff08;本课程中如有涉及代码或硬件架构&#xff0c;则对应的版本号&#xff1a;TF-A 2.80&#xff0c;optee 3.20, Linux Kernel 6.3&#xff0c;armv8.79.0的aarch64&#xff09; &#xff08;注意&#xff1a; 该课程没有PPT&#xff0c;该课程是对照代码讲解的&#x…

回归预测 | Matlab基于ABC-SVR人工蜂群算法优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于ABC-SVR人工蜂群算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于ABC-SVR人工蜂群算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于ABC-SVR人工蜂群算法优化支持…

矩阵重叠问题判断

创作背景 看到一道题目有感而发想写一篇题解&#xff0c;涉及的是一种逆向思维 桌面窗体重叠 - 洛谷https://www.luogu.com.cn/problem/U399827题目来源于《信息学奥赛课课通》 大致就是给一个长方形的左上顶点坐标&#xff08;x1,y1&#xff09;和右下顶点坐标&#xff08;x…

面试题:SpringBoot项目怎么设计业务操作日志功能?

文章目录 前言需求描述与分析系统日志操作日志 设计思路Spring AOPFilter和HandlerInterceptorSpringAOP、过滤器、拦截器对比 实现方案环境配置依赖配置表结构设计代码实现 测试调试方法验证结果 总结 前言 很久以前都想写这篇文章&#xff0c;一直没有空&#xff0c;但直到现…

【QT+QGIS跨平台编译】之一:【sqlite+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、sqlite3介绍二、文件下载三、文件分析四、pro文件五、编译实践 一、sqlite3介绍 SQLite是一款轻型的数据库&#xff0c;是遵守ACID的关系型数据库管理系统&#xff0c;它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的&…

MSVS C# Matlab的混合编程系列2 - 构建一个复杂(含多个M文件)的动态库:

前言: 本节我们尝试将一个有很多函数和文件的Matlab算法文件集成到C#的项目里面。 本文缩语: MT = Matlab 问题提出: 1 我们有一个比较复杂的Matlab文件: 这个MATLAB的算法,写了很多的算法函数在其他的M文件里面,这样,前面博客的方法就不够用了。会报错: 解决办法如下…

[学习笔记]刘知远团队大模型技术与交叉应用L3-Transformer_and_PLMs

RNN存在信息瓶颈的问题。 注意力机制的核心就是在decoder的每一步&#xff0c;都把encoder的所有向量提供给decoder模型。 具体的例子 先获得encoder隐向量的一个注意力分数。 注意力机制的各种变体 一&#xff1a;直接点积 二&#xff1a;中间乘以一个矩阵 三&#xff1a;…

Opncv模板匹配 单模板匹配 多模板匹配

目录 问题引入 单模板匹配 ①模板匹配函数: ②查找最值和极值的坐标和值: 整体流程原理介绍 实例代码介绍: 多模板匹配 ①定义阈值 ②zip函数 整体流程原理介绍 实例代码: 问题引入 下面有请我们的陶大郎登场 这张图片是我们的陶大郎,我们接下来将利用陶大郎来介绍…