Unity3d: 资源释放时存储空间不足引发的思考和遇到的问题
手机游戏第一次启动基本上都会做资源释放的操作,这个时候需要考虑存储空间是否足够,但是Unity没有自带获取设备存储空间大小的
接口,需要调用本地方法分别去android或ios获取,这样挺麻烦的。而且资源释放是耗时的,这个时候,如果其他应用在做后台下载,万一
把空间占完了怎么办呢。可能有人会想到释放资源的时候每一次写操作都获取一次存储空间的大小,这样当然能解决问题,但是同时也极大的
延长了资源释放的时间,毕竟每一次本地方法的调用还是比较耗时的。
既然调用本地方法麻烦又耗时,那有没有什么方法不用调用本地方法呢?
首先,我尝试了一下直接不做任何判断,只加了一个try catch,看看空间满了能不能捕获到异常。当然第一件事是把手机给塞满。嗯,我的安卓机还有8G的存储空间,那我用程序创建几个1G的空文件吧。
int buffer = 1024 * 1024 * 1024; using (var tempStream = File.Create(tempPath)) { tempStream.SetLength(buffer); tempStream.Flush(); tempStream.Close(); }
文件创建出来了,然后查看文件的大小确实是1GB。然后再去设置里面查看设备的剩余空间,还是8G。嗯?不对。我再创建8个文件,还是不对。
看来占位文件是不计大小的,虽然看起来有那么大。。。那么改一下生成方式,老老实实写文件
int buffer = 1024 * 1024 * 1024; using (var tempStream = File.Create(tempPath)) { tempStream.Write(new byte[buffer], 0, buffer); //tempStream.SetLength(buffer); tempStream.Flush(); tempStream.Close(); }
这次对了,虽然慢了点,但也比从外部拷贝文件到手机里快多了。既然方法对了那就多创建几个文件,当创建到第9个文件时,catch到异常,提示的 "Disk full XXX"。看来完全可以通过try catch的方式来判断磁盘空间是否够用嘛。当然catch到的不一定都是Disk full的异常,也有可能是out of memory。但是总归是释放资源失败了,要给玩家一个交代,那么统一提示为 “释放资源失败,可能磁盘空间满了“,然后这个时候自动上报异常,去后台查看就好了(我是用的Bugly)。如果你给玩家提示一个什么 “释放资源失败,内存溢出了“,玩家反而是一脸懵逼。
所以,完全可以用try catch来替换通过本地方法获取磁盘存储空间的方式。
我把获取本地方法的地方给去掉,加了一个try catch,然后运行游戏,释放资源
xxxxxxxx .... try { fileName = Path.Combine(unRootPath, fileName); fileName = fileName.Replace('\\', '/'); using (FileStream streamWriter = File.Create(fileName)) { WriteFile(s, streamWriter); streamWriter.Flush(); streamWriter.Close(); } ++nWriteCount; } catch (Exception ex) { UpdateLog.ERROR_LOG(ex.Message); return; } .... xxxxxxx
protected int _bufferSize = 1024; protected byte[] _buffer = new byte[1024]; private void WriteFile(ZipInputStream zi, FileStream outFile) { int size = 0; while((size = zi.Read(_buffer, 0, _bufferSize)) > 0) { outFile.Write(_buffer, 0, size); } }
等待资源释放触发catch,但是,恐怖的事情发生了。游戏闪退了,闪退了,闪退了。。。
我查了下log,catch信息是打印出来了,也是 Disk full,为啥游戏就闪退了?刚刚还写了测试代码用来创建大文件,都没有闪退呀。反复比较了一下代码,发现是bufferSize不一样,一个1k,一个1G。是这个引起的?我把bufferSize改成2k,不行。4k, 不行,6k,还是不行。1MB,可以了~~~隐约记得socket底层的buffer貌似是8kb,难道写文件操作内部也有个8kb的buffer。我把size改成10k,也没闪退。看来还真有可能。
反编译System.IO.FileStream这个dll,还真有一个默认8kb的设置
看到这个代码,buffer大于8k和小于8k,底层写文件走的逻辑是不同的,在if里面会抛出写文件失败的异常,else里面可能就会卡死。当然这也是推测,哪位大神知道原因的,麻烦告知一下。
好了,把buffer设置为10k,一样没有闪退,完美触发异常,弹出友好提示。
protected int _bufferSize = 1024 * 10;
protected byte[] _buffer = new byte[1024 * 10];
总结:通过try catch来捕获磁盘空间不足是可行的,但是需要写文件的bufferSize大于8k, 太大没必要,10k比较合适。