笔记|Unity异步处理与UI Text显示的问题
前言
这阵子一有空就在研究Unity3D网络通讯,使用过程中访问通过协程的方式收到返回的数据直接更新Text的显示值都没有问题,结果在处理Socket通讯TCP方式采用异步时遇到了问题,本章主要就是记录一下测试的过程和处理方法,关于Unity3D与后台的网络通讯这块后面会有一个系列发出来。
遇到的问题
上图中可以看到,我们首先调用的是Restful正常的Get,Post的方法,获取到的数据在屏幕上Text的组件中也正常显示了。
再看我们使用Socket中的TCP通讯,当服务器发送数据过来后,上图中左下角的输入日志已经接收到了服务器发送的88878的数据,但是屏幕中的Text组件并没有更新显示。
先说明上面所有的网络请求后,返回更新显示Text的值都是用的同一个Action的委托方法
/// <summary>
/// 写返回Action的处理方法
/// </summary>
private void InitAction()
{
actionRes = new Action<bool, string>((bl, str) =>
{
Debug.Log(str);
if (bl)
{
txtshow.text = str;
}
else
{
string resjson = "{"array":" + str + "}";
_showstr = resjson;
WeatherData lists = JsonUtility.FromJson<WeatherData>(resjson);
StringBuilder sb = new StringBuilder();
foreach (WeatherForecast item in lists.array)
{
sb.Append("Date:" + item.Date + " Summary:" + item.Summary + " TemperatureF:"
+ item.TemperatureF + "TemperatureC:" + item.TemperatureC + "rn");
}
txtshow.text = sb.ToString();
}
});
}
问题排查
微卡智享
即然我们回调方法都一样,那我们就看看WebApi和Socket的通讯有什么不一样。
WebApi调用
上图中可以看到我们访问HttpRestful的Get方法里面是用协程的操作完成的,当请求返回数据后,直接调用action后就是我们前面代码的回调函数更新显示,接下来我们再看看Socket TCP的通讯。
Socket TCP通讯
上图中,我们使用Socket的TCP接收时,首先定义了一个TransData的类,把action传入进去,然后通过NetworkStream的BeginRead的方法进行处理数据接收。
TransData的类结构
上面几个图就是BeginRead中加入的回调函数,在接收完后我们直接调用transData类中的actionResult方法做后续的处理。
问题思考
不说两个方法接收数据后的处理,这里肯定都是一样的,最终都是把接收到的返回结果调用Action回调方法中执行,那问题会出来哪呢?
仔细再看了一个,在Restful的请求里面,我们用的是协程的方式处理的,而在Socket Tcp中,我们的BeginRead是一个异步的线程处理的,搜索了一下Unity中的协程解析,有这第一段说:
协程的作用一共有两点:1)延时(等待)一段时间执行代码;2)等某个操作完成之后再执行后面的代码。总结起来就是一句话:控制代码在特定的时机执行。
很多初学者,都会下意识地觉得协程是异步执行的,都会觉得协程是C# 线程的替代品,是Unity不使用线程的解决方案。
所以首先,请你牢记:协程不是线程,也不是异步执行的。协程和 MonoBehaviour 的 Update函数一样也是在MainThread中执行的。使用协程你不用考虑同步和锁的问题。
从上面这段话来说,协程不是异步执行的,所以text更新可以直接显示,而使用BeginRead时是异步线程操作的,做过多线程开发的同学应该都处理过线程与UI进行同步的问题(Andorid的开发可能更多),接下来我们就直接做个验证看看。
测试验证
我在Tcp通讯的Recv方法里面,使用BeginRead异步处理之前,先调用一下Action的方法,看看效果怎么样。
从上图中可以看到,在进入BeginRead之前,我们直接调用action的方法后,Text也是直接显示出来的没有问题了,这就验证了上面所说的问题,所以我们下一步就考虑怎么处理线程和UI同步的问题即可。
解决办法
微卡智享
其实找到问题后,解决这个的方法也更简单了,因为Unity中本身就有Update(),OnGUI()等方法,在每帧执行,所以我们可以直接把返回的数据做为一个内部变量,然后判断这个变量是否修改了,修改后再在相关的方法中更新Text即可。
01
加入更新显示变量
增加两个变量,一个是返回值保存到_showstr中,另一个是bool类型的,每当_showstr改变时,更改这个改变的值。
02
修改Action的赋值
修改Action的方法,把原来的txtshow.text赋值屏蔽后,改为返回的字符串赋值给_showstr,并且把_isshowstrupd的值改为True,用于记录当前显示值已经更新。
03
OnGUI中更新显示
然后在OnGUI方法中,判断如果_isshowstrupd为true时,修改txtshow.text的赋值更新,再把_isshowstrupd=false;
通过上面这几步就解决Text的显示问题了。
实现效果
- 【转载】记Golang数据库查询封装的坑
- Golang 基本语法学习笔记之流程控制
- Go 语言的演化历程
- JS 评分五角星随鼠标移动显示
- Golang标准库学习——buffio包 ---转
- 【Go 语言社区】Go语言条件变量的两个例子
- mysqlimport导入报错的排查(r10笔记第58天)
- 【Go 语言社区】POJ 1047 Round and Round We Go 循环数新解
- 【Go 语言社区】删除redis所有KEY
- 【Go 语言社区】Golang 动态实例化结构体
- 【Go 语言社区】Go 错误处理
- 【Go 语言社区】Go 语言范围(Range)
- 【Go 语言社区】JS 相关---Window Location
- 【Go 语言社区】Go 语言Map(集合)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Laravel 修改默认日志文件名称和位置的例子
- php 命名空间(namespace)原理与用法实例小结
- Laravel Eloquent ORM 实现查询表中指定的字段
- Yii框架 session 数据库存储操作方法示例
- 使用laravel指定日志文件记录任意日志
- php实现微信小程序授权登录功能(实现流程)
- PHP封装请求类实例分析【基于Yii框架】
- php 多继承的几种常见实现方法示例
- PHP中通过getopt解析GNU C风格命令行选项
- laravel高级的Join语法详解以及使用Join多个条件
- laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
- 使用laravel的Eloquent模型如何获取数据库的指定列
- php 使用 __call实现重载功能示例
- laravel excel 上传文件保存到本地服务器功能
- PHP用swoole+websocket和redis实现web一对一聊天