游戏的热更需求
在游戏开发中,热更并下载资源,对商业化游戏来说是一个必须的需求。而想要高效并稳定地下载文件一直是开发中的一个痛点。我们能看到市面上,大量游戏都在下载上卡死,断网,重启无效等等问题。我们来总结一下要解决的问题:
网络请求异常处理
——断网、请求失败、请求超时、网络波动、下载到一半等问题文件读写异常处理
——文件读失败、文件写失败、文件写一半等问题游戏进程异常处理
——下载到一半、文件写一半、玩家退出等问题重启现场恢复处理
——可恢复性,继续下载文件的下载正确性
——文件长度、文件校验,文件可读性文件线程高效下载
——多线程,异步回调文件
上述还只是针对下载会出现的问题,对于版本管理导致的问题,下一篇文章再讨论。
下文会为上述的每个问题,提供对应的解决方案。
下载管理器
下图是下载管理器的结构,外部只暴露三个函数,负责异步下载
,同步下载
,下载回调刷新
内部对同步下载,直接返回结果。
内部实现对异步下载创建线程,对外部隐藏异步线程。
上述异步回调实际上是通过主线程调用DownloadUpdate来遍历的同步回调
。
TIP:为什么设计成Update里同步回调呢?
如果直接通过异步线程进行回调,那么在回调函数里必须要处理
线程锁
的问题和回调可能运行逻辑报错
的问题,这样势必造成代码的耦合度增加。
而通过主线程调用Update来回调,可以隐藏了线程逻辑,也可以保证主线程的线性运行。
下载请求数据结构
public delegate void DonwloadErrorCallBack(DownloadUnit downUnit, string msg);
public delegate void DonwloadProgressCallBack(DownloadUnit downUnit, int curSize, int allSize);
public delegate void DonwloadCompleteCallBack(DownloadUnit downUnit);
public class DownloadUnit
{
public string name; //下载的文件,作为标识
public string downUrl; //远程地址
public string savePath; //本地地址
public int size; //文件长度,非必须
public string md5; //需要校验的md5,非必须
public bool isDelete; //用于清理正在下载的文件
public DonwloadErrorCallBack errorFun;
public DonwloadProgressCallBack progressFun;
public DonwloadCompleteCallBack completeFun;
}
数据结构主要提供了下载必要的参数和常用的三个回调函数。
下载管理器框架代码
由上述,可以写出如下的一个代码框架
public class DownloadMgr
{
private static DownloadMgr _Instance = null;
public static DownloadMgr I
{
get
{
if (_Instance == null) _Instance = new DownloadMgr();
return _Instance;
}
}
private DownloadMgr()
{
}
//异步会创建线程
public void DownloadAsync(DownloadUnit info)
{
}
//同步不会调用回调函数,返回是否成功
public bool DownloadSync(DownloadUnit info)
{
}
//请将Updata放到主线程Update中
public void Update()
{
}
}
多线程运行逻辑
很多人可能会有一个简单处理方法,一个文件一个线程,运行结束就销毁。
too young too simple
C#中一个线程要占用1M的内存,而在热更中,几千个小文件轻轻松松的。
那么,我们需要解决几个问题:
线程的数量上限
——内存占用上限,同时运行数量线程的重复利用
——降低创建销毁开销线程内存释放
——下载结束,不占用内存
很简单的,我们就能想到生产者-消费者
模型