Unity建立全局异常处理机制

来源:互联网 发布:乃知别是一家 编辑:程序博客网 时间:2024/05/04 01:57
我们知道,Unity的异常处理做得非常好,源于他在框架底层会自动捕获异常,所以一般的异常(比如空引用、除0操作之类)均不会导致整个进程crash掉,原因很简单,代码在try段中发生了异常,在catch段处理以后,表现在Unity编辑器中便是在日志窗口打印红色错误日志,而在已经发布的项目中,由于异常依然被Unity捕获,所以进程并不会crash掉,但对于我们而言他是未知的,这个异常很明显会导致程序在功能表现上的不正确。

那么我们需要的便是在发生任何未知异常的时候,获取异常的相关信息,用户的反馈信息,同时希望结束掉整个程序,毕竟发生了未知的异常即便他无关紧要,但总会导致某些功能的不正常表现。

好在Unity对于这方面的封装非常完善,我们完全可以借助他自身的异常处理机制来完善我们的需求。

如下:

public static void RegisterLogCallback( Application.LogCallback handler );

RegisterLogCallback是Application中的静态方法,旨在将LogCallback类型的函数注册给Unity的日志回调委托,在Unity输出任何日志的时候都将会调用到方法handler。

在handler中我们就可以截获任何的日志类型(这里的日志包括Debug.Log输出的日志,以及Unity自身在捕获到异常时会打印的异常日志),判断其为我们未知的异常,同时就可以做出我们自己的处理,包括在这里结束掉整个程序并弹出一个友好的bug反馈窗口。

委托LogCallback的形式为:

public delegate void LogCallback( string condition, string stackTrace, LogType type );

 其中condition为日志内容,stackTrace为相关的堆栈调用信息,type为日志的类型,日志类型分为以下五种:

public enum LogType    {        Error = 0,        Assert = 1,        Warning = 2,        Log = 3,        Exception = 4,    }

         1、Error为错误日志,Debug.LogError输出的日志便是此类型;
2、Assert为Unity本身的异常,这种异常通常都是致命的,会导致整个进程crash;
3、Warning为警告日志,Debug.LogWarning输出的日志便是此类型;
4、Log为普通日志,Debug.Log输出的日志便是此类型;
5、Exception为被Unity捕获的未知异常,也就是我们自己的代码产生的异常,这将是我们处理的重点对象。


如此,我们将代码完善一下:

using UnityEngine;using System;using System.IO;using System.Diagnostics;using System.Collections;public class ExceptionHandler : MonoBehaviour {    //是否作为异常处理者    public bool IsHandler = false;    //是否退出程序当异常发生时    public bool IsQuitWhenException = true;    //异常日志保存路径(文件夹)    private string LogPath;    //Bug反馈程序的启动路径    private string BugExePath;void Awake(){        LogPath = Application.dataPath.Substring( 0, Application.dataPath.LastIndexOf( "/" ) );        BugExePath = Application.dataPath.Substring( 0, Application.dataPath.LastIndexOf( "/" ) ) + "\\Bug.exe";        //注册异常处理委托        if( IsHandler )        {            Application.RegisterLogCallback( Handler );        }}    void OnDestory() {        //清除注册        Application.RegisterLogCallback( null );}    void Handler( string logString, string stackTrace, LogType type )    {        if( type == LogType.Error || type == LogType.Exception || type == LogType.Assert )        {            string logPath = LogPath + "\\" + DateTime.Now.ToString( "yyyy_MM_dd HH_mm_ss" ) + ".log";            //打印日志            if( Directory.Exists( LogPath ) )            {                File.AppendAllText( logPath, "[time]:" + DateTime.Now.ToString() + "\r\n" );                File.AppendAllText( logPath, "[type]:" + type.ToString() + "\r\n" );                File.AppendAllText( logPath, "[exception message]:" + logString + "\r\n" );                File.AppendAllText( logPath, "[stack trace]:" + stackTrace + "\r\n" );            }            //启动bug反馈程序            if( File.Exists( BugExePath ) )            {                ProcessStartInfo pros = new ProcessStartInfo();                pros.FileName = BugExePath;                pros.Arguments = "\"" + logPath + "\"";                Process pro = new Process();                pro.StartInfo = pros;                pro.Start();            }            //退出程序,bug反馈程序重启主程序            if( IsQuitWhenException )            {                Application.Quit();            }        }    }}

我们的处理机制是当接收到LogType.Error、LogType.Exception、LogType.Assert 类型的日志输出请求时,就将日志信息打印到本地文件,同时主程序退出,启动bug反馈程序,我这里的bug反馈程序是一个winform窗口程序,仿照了类似QQ的报错界面:



Bug反馈程序会接收一个参数:日志文件的路径,以便于能够在反馈界面将用户输入的引起异常的原因添加到错误日志中,用户选择发送错误报告则会将该错误日志上传到我们的服务器,也就是说如果不提供参数,直接运行bug反馈程序是不会成功的。

我们在Unity中新建一个测试脚本Test.cs,输入以下内容:

using UnityEngine;using System.Collections;public class Test : MonoBehaviour {    public GameObject TestObj;    void OnGUI()    {        if( GUILayout.Button( "点我就会抛出一个异常" ) )        {            TestObj.transform.position = Vector3.one;        }    }}

 这里的TestObj我们不赋值,也就是说OnGUI里面的调用将会报空引用错。

我们将项目发布,同时将bug反馈程序拷入到exe的同级目录:


我们运行Test.exe,点击屏幕左上角的按钮:


这时引发异常,主程序会直接退出,bug反馈程序启动,我们可以查看错误的日志信息:


我们可以看到一个并未被我们手动用try catch处理的异常被捕获了,这样的机制很明显省去了我们苦心积虑的研究哪个地方该用try catch的苦恼,因为所有的异常都不会被测试遗落同时会展现出非常详细的错误日志,以便于我们修改,当然用户的反馈也是解决这些bug的重大保障。
不过针对平台的不同,比如移动端可能并不能提供一个外部bug反馈程序,但只要能够捕获到这些未知的异常,接下来怎么做都可以,比如可以在程序里直接弹出一个窗口提示报错啦,然后用户填写相关错误反馈,点击确定之后后台提交反馈,同时程序自动重启,如果认为界面或者内容上的一些由异常引发的功能表现不正确无关紧要的话,这里也不用重启,毕竟只要不是Assert类型的异常,Unity都不会crash。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 泥石流来了怎么办教案 墙壁插座不通电怎么办 当前目录不能用怎么办 海鲜过敏怎么办没有药 哺乳后胸变小怎么办 有脑出血前兆该怎么办 体检发现甲状腺结节怎么办 月子里宝宝感冒怎么办 10天新生儿感冒怎么办 20天新生儿感冒怎么办 新生儿20天鼻塞怎么办 25天新生儿感冒怎么办 42天新生儿感冒怎么办 上呼吸道感染怎么办比较好 小猫咪太调皮怎么办 水晶彩泥弄到衣服上怎么办 进境动植物检疫许可怎么办 跳舞不会听拍子怎么办 税盘丢了注销公司怎么办 认缴资金不到位怎么办 同一单元有凶宅怎么办 有地皮没房产证怎么办 社保资金被侵吞怎么办? 集体计件手脚慢怎么办 发票当月没用完怎么办 非工业用地怎么办环评 商标注册途中英文错误怎么办 孩子的英文不好怎么办 高盛英文不好怎么办 去美国英文不好怎么办 去越南不会英语怎么办 法斗得了毛囊炎怎么办 头发里有毛囊炎怎么办 笔记本画cad慢怎么办 面试打不出问题怎么办 ai撤销多了怎么办 卖钢材没客户怎么办 手抓饼煎的很硬怎么办 微信朋友圈侵权怎么办 tekla工具栏显示不全怎么办 收银员收到假钱怎么办