当前位置: 首页 > wzjs >正文

企业为何做网站涿州市查建设局网站

企业为何做网站,涿州市查建设局网站,同城推广,18款禁用网站app直播Unity动态列表UniTask异步数据请求 很久没有写东西了。最近有一个需求,在Unity项目里,有几个比较长的列表,经历了一翻优化,趁这几日闲暇,记录下来,给自己留个笔记,也送给有缘之人共同探讨吧。 …

Unity动态列表+UniTask异步数据请求

很久没有写东西了。最近有一个需求,在Unity项目里,有几个比较长的列表,经历了一翻优化,趁这几日闲暇,记录下来,给自己留个笔记,也送给有缘之人共同探讨吧。
以其中一个列表为例,其大体需求是:首先向后台请求列表数据,后台会反馈一个列表,本质上是数据的id数组。然后,由于数据项很复杂,所以要知道某条信息的具体数据,还要以id为参数,再次向后台请求关于这条数据的具体数据。即:

  • 第一次只请求列表数据:
    Reuest:getCableList?key=filter
    Response:{ success: true, message: null, datas: [ 1, 2, 5, 10, 12 ] }
  • 第二次请求详细数据:
    Request: getCableData?id=5
    Response: { success: true, message: null, data: [{ name: “西线机房至汇聚光交箱”, descript: “Some Text” …},…]}

一、异步加载

现成的有UniTask和协程方案,学习了下前人总结的UniTask之后,感觉UniTask比协程好:第一,性能好,更少的GC;第二,更灵活,体现在能很方便的做取消、超时管理、提供更多的yield时机选择、而且还不需要MonoBehaviour;第三,写起来代码来更人性化;第四,免费,没有额外的代价。所以,UniTask确实很好。
第一个版本的代码如下,主要思路是:从后台请求列表数据,获取到列表之后,分批进行第二次请求,然后将数据加载到列表中,实例化Item并更新UI。

// 用POST方法请求文本数据
private static async UniTask<string> RequestTextWithPostMethod(string url, Dictionary<string, string> data, float waitTime=3f)
{try{CancellationTokenSource cts = new CancellationTokenSource();cts.CancelAfterSlim(TimeSpan.FromSeconds(waitTime));var request = await UnityWebRequest.Post(url, data).SendWebRequest().WithCancellation(cts.Token);return request.result == UnityWebRequest.Result.Success ? request.downloadHandler.text : null;}catch{return null;}
}private readonly ConcurrentDictionary<int, CableItem> activeItems = new();
// 更新列表数据
private void UpdateList(string key)
{try{// 向后台请求列表数据string json = await RequestTextWithPostMethod("http://demo.myhost.com/unity/getCableList",new Dictionary<string, string> { { "key", key } });if (string.IsNummOrEmpty(json))throw new Exception(serverErrorMessage);var res = JsonConvert.DeserializeObject<CableListResponse>(json);if (!res.success)throw new Exception(res.message);HashSet<int> ids = new(res.datas);// 如果已实例化的项不在请求结果中,则清除它们foreach (var aid in activeItems.Keys.Where(aid => !ids.Contains(aid))){itemPool.Release(activeItems[aid]);activeItems.TryRemove(aid, out _);}int allCount = res.datas.Count;int total = 0;// 每5个为一批,按批次异步请求数据,避免并发量太大foreach (var chunk in res.datas.Chunk(5)){var tasks = chunk.Select(async id =>{// 如果该ID未在活动列表中,则实例化该项if (!activeItems.TryGetValue(id, out CableItem item)){item = cableItemPool.Get();activeItems.TryAdd(id, item);item.transform.SetAsLastSibling();}// 发起第二次请求,将获取到的数据设置到Itemawait GetCableData(id).ContinueWith(cable =>{item.SetCableData(cable);});GlobalProgressBar.SetValue(0.1f + 0.9f * (++total / (float)allCount));});// 如果该批次已完成,则下一帧发起下一个批次await UniTask.WhenAll(tasks);await UniTask.Yield();}}catch (Exception e){errorMessageText.text = e.Message;}finally{isRequsting = false;}
}

经测试,上述代码确实挺好,在数据请求时,对帧率几乎没有影像。但是,它还是不够好,当列表非常大时,更新一次数据总体上还是需要很久,更要命的是,由于它列表项太多,使用原生的Scroll View组件会严重影像性能,当切换UI页面(需要关闭或激活ScrollView时操作明显有粘滞感)。想到的解决方案有二:

  • 其一,分页。每次之请求一部分数据,肯定能改善操作,但是需要后台也同步改为分页支持,而且需要增加上一页、下一页、页面展示等按钮还有逻辑,还会让操作更复杂,不太符合原需求。
  • 其二,优化ScrollView。必然选这个。

二、动态Scroll View

这并不是我的首创,早有各种大神实现过了。它思路很简单,Scroll View同时可视的Item是有限的,只需要保证能看见的Item处于激活态就好,其余的可以禁用掉。进一步优化下就是,保留可视列表项的前几个和后几个项激活,以便优化滚动。首先实现一个动态的超级ScrollView:

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.UI;namespace HXDynamicScrollView
{[RequireComponent(typeof(ScrollRect))]public abstract class DynamicScrollView<TData> : MonoBehaviour{[SerializeField] private DynamicScrollItem<TData> ItemPrefab;public float ItemHeight = 80f;		// 项的高度public float ItemSpacing = 10f;		// 项之间的间距public int PreheatCount = 10;		// 预加载可视列表附近的几个Itempublic TData[] DataArray { get; private set; }		// 数据列表public int totalItemCount => DataArray?.Length ?? 0;private ScrollRect m_scrollRect;private RectTransform m_viewport;private RectTransform m_content;private float contentHeight;private float ItemHeightWithSpcaing => ItemHeight + ItemSpacing;  // 每个项包括间距的高度private int currentFirstIndex = -1;private int currentLastIndex = -1;private readonly Dictionary<int, DynamicScrollItem<TData>> activeItems = new();  // 活动的项private ObjectPool<DynamicScrollItem<TData>> itemPool;	// Item对象池public delegate void OnItemInstancedHander(DynamicScrollItem<TData> item);public event OnItemInstancedHander OnItemInstanced;public delegate void OnItemActivedHandler(DynamicScrollItem<TData> item);public event OnItemActivedHandler OnItemActived;public delegate void OnItemRecycledHandler(DynamicScrollItem<TData> item);public event OnItemRecycledHandler OnItemRecycled;public delegate void OnBeforeItemDataChangedHander(TData[] datas);public event OnBeforeItemDataChangedHander OnBeforeItemDataChanged;private void Awake(){itemPool = new ObjectPool<DynamicScrollItem<TData>>(() =>{var item = Instantiate(ItemPrefab, m_content);OnItemInstanced?.Invoke(item);return item;},item =>{item.gameObject.SetActive(true);OnItemActived?.Invoke(item);},item =>{OnItemRecycled?.Invoke(item);item.gameObject.SetActive(false);},item => Destroy(item.gameObject));m_scrollRect = GetComponent<ScrollRect>();m_viewport = m_scrollRect.viewport;m_content = m_scrollRect.content;m_scrollRect.onValueChanged.AddListener(OnScrollViewChanged);m_content.anchorMin = new Vector2(0, 1);m_content.anchorMax = new Vector2(1, 1);m_content.pivot = new Vector2(0.5f, 1);m_content.sizeDelta = new Vector2(0, 0);}// 滚动条滚动事件private void OnScrollViewChanged(Vector2 _){UpdateVisibleItems();}// 设置数据项public void SetDataList(IEnumerable<TData> dataList){if(DataArray is {Length: > 0 })OnBeforeItemDataChanged?.Invoke(DataArray);DataArray = dataList.ToArray();CalculateContentHeight();UpdateVisibleItems(true);}// 计算内容高度private void CalculateContentHeight(){contentHeight = totalItemCount * (ItemHeight + ItemSpacing) + ItemSpacing;m_content.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, contentHeight);}// 更新可视的项private void UpdateVisibleItems(bool bForce = false){// 如果数据是空的,则清理现存的并直接返回if (DataArray is not { Length: > 0 }){foreach (var item in activeItems){item.Value.Hide();itemPool.Release(item.Value);}activeItems.Clear();return;}var viewportTop = m_content.anchoredPosition.y;var viewportBottom = viewportTop + m_viewport.rect.height;// 计算可视项前后预加载项的索引int newFirstIndex = Mathf.Max(0,Mathf.FloorToInt((viewportTop - PreheatCount * ItemHeightWithSpcaing) / ItemHeightWithSpcaing));int newLastIndex = Mathf.Min(totalItemCount - 1,Mathf.CeilToInt((viewportBottom + PreheatCount * ItemHeightWithSpcaing) / ItemHeightWithSpcaing));// 如果不需要更新则返回if (!bForce && currentFirstIndex == newFirstIndex && currentLastIndex == newLastIndex)return;// 清理需要删除的项List<int> toRemove = new();foreach (var item in activeItems.Where(item => item.Key < newFirstIndex || item.Key > newLastIndex)){item.Value.Hide();itemPool.Release(item.Value);toRemove.Add(item.Key);}foreach (var index in toRemove)activeItems.Remove(index);// 激活可视或可视附近的,即需要预加载的项for (int i = newFirstIndex; i <= newLastIndex; i++){if (!activeItems.ContainsKey(i)){var item = itemPool.Get();item.SetDataAndShow(DataArray[i]);PlaceItem(i, item);activeItems.Add(i, item);}}currentFirstIndex = newFirstIndex;currentLastIndex = newLastIndex;}// 放置项private void PlaceItem(int index, DynamicScrollItem<TData> item){float yPos = -index * ItemHeightWithSpcaing - ItemSpacing;item.anchoredPosition = new Vector2(0, yPos);}}
}
using UnityEngine;
using UnityEngine.EventSystems;namespace HXDynamicScrollView
{public abstract class DynamicScrollItem<TData> : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler{public virtual void Hide(){}public abstract void SetDataAndShow(TData data);public Vector2 anchoredPosition{get => ((RectTransform)transform).anchoredPosition;set=> ((RectTransform)transform).anchoredPosition = value;}protected bool IsMouseHover { get; private set; }public virtual void OnPointerEnter(PointerEventData eventData){IsMouseHover = true;}public virtual void OnPointerExit(PointerEventData eventData){IsMouseHover = false;}}
}

上面代码就是全部的超级ScrollView了。
以上面的CableList为例,使用它,变成异步动态加载的超级列表。每次滚动时,只会有非常少量的项被激活,所以无需分批,直接异步求情数据即可。

public class CableItem : DynamicScrollItem<int>
{// 这里省略了一些其他的代码public CableData Data { get; private set; }private void SetCableData(CableData data){Data = data;if (data != null){// 将数据显示到UI组件上}}// 当Item处于预加载或可视时,被调用,请求数据public override void SetDataAndShow(int data){UpdateData(data).Forget();}private async UniTaskVoid UpdateData(int id){var cableInfo = await GetCableData(id);if(cableInfo!=null)SetCableData(cableInfo);}private static async UniTask<CableData> GetCableData(int id){// 如果数据已存在,并且数据处于有效期内,则直接返回if (cables.TryGetValue(id, out CableData cable)){if (cable.IsEditing || Time.time - cable.lastUpdatetime < 300f)return cable;}// 向后台请求数据var json = await RequestTextWithPostMethod(urlGetCableInfo,new Dictionary<string, string> { { "id", id.ToString() } });try{if (string.IsNullOrEmpty(json))throw new Exception(serverErrorMessage);var cableData = JsonConvert.DeserializeObject<CableInfo>(json, JsonSettings);if (!cableData.success)throw new Exception(cableData.message);cableData.data.lastUpdatetime = Time.time;cableData.data.IsEditing = false;cables.AddOrUpdate(id, cableData.data, (_, _) => cableData.data);return cableData.data;}catch{return null;}}
}

上述Item还有进一步优化的空间,如,极端情况下,滚动速度很快,或网络情况不好的情况下,可能数据请求还未返回,Item就由可是状态变为非可视状态,此时,可以很容易的增加取消机制。在disable中进行取消即可。

结论

同时,项目还采取了其他的优化机制,比如,使用数据缓存,请求过的数据,在一定时间内再次使用时无需再次请求,还有使用对象池等奇数,经过上述优化后,项目中的列表非常丝滑,加载无感,进度条也删去了。


文章转载自:

http://cDR7mnWU.mkzdp.cn
http://oqyiclkO.mkzdp.cn
http://bsS6V6h3.mkzdp.cn
http://8hjJ4EdT.mkzdp.cn
http://kWWMWnVs.mkzdp.cn
http://chD1jVfX.mkzdp.cn
http://O1IF0PfU.mkzdp.cn
http://IVtzkN9P.mkzdp.cn
http://Xx5Usr8M.mkzdp.cn
http://1vQamscA.mkzdp.cn
http://3NlZXw6b.mkzdp.cn
http://zCgbpgKp.mkzdp.cn
http://kZyH3uhE.mkzdp.cn
http://93CPlqkX.mkzdp.cn
http://m3QIbxbF.mkzdp.cn
http://c1ziEoS4.mkzdp.cn
http://0vdAUUJL.mkzdp.cn
http://LVAbRaM8.mkzdp.cn
http://q29rluYH.mkzdp.cn
http://hGwTETbZ.mkzdp.cn
http://hUeNvLWa.mkzdp.cn
http://iOcmsGVW.mkzdp.cn
http://LKjjolTW.mkzdp.cn
http://a6axqzNa.mkzdp.cn
http://vJaIffz6.mkzdp.cn
http://dpqY9S3W.mkzdp.cn
http://PyabX874.mkzdp.cn
http://VKsXirhd.mkzdp.cn
http://wYI6sVOe.mkzdp.cn
http://euLpZ55h.mkzdp.cn
http://www.dtcms.com/wzjs/769143.html

相关文章:

  • 用rp怎样做网站网站建设沟通
  • 河北省建设厅网站重新安装安康企业网站建设价格
  • 网站建设广告宣传素材陕西网站推广公司
  • 企业网站建设不要空间可以吗沈阳市建设工程项目管理中心
  • 网站定制报价表wordpress自定义字段不显示
  • 北京做胃镜哪好德胜门网站I视频软件观看免费高清下载
  • 食堂网站建设方案南宁市网站开发公司
  • 网站百度快照更新营销型网站建设就找山东艾乎建站
  • 合肥网站维护公司去掉自豪地采用wordpress
  • 怎么优化网站排名学习网站开发体会与感想
  • 哈尔滨建设银行网站首页厦门关键词优化企业
  • 招聘网站开发源码网站开发项目 工作分解图
  • 想做个网站怎么做如何建立自己的商城
  • 西安学校网站制作05网英语书
  • 网站制作需求文档镇江网站建设镇江
  • 做网批的网站免费公司logo设计图
  • 惠州营销网站建设保定徐水网站建设
  • 网站营销话术wordpress模板网
  • 华为云怎么建网站邮箱地址注册入口
  • 国产前端框架 做网站搜狗提交网站入口
  • 南宁网站建设培训班泉州网站建设-泉州网站建设公司
  • 邯郸做网站的地方跨境电商平台有哪些免费的
  • 企业网站管理系统cms源码下载深圳投资公司实力排行榜
  • 网站建设公司多少钱关注公众号在哪里查找
  • 最火高端网站设计厂家邹城房产信息网
  • 官方网站有哪些南京网站制作有限公司
  • 企业做网站有什么好处上海奉贤做网站
  • 张家口网站建设vewan提供电商网站建设
  • 苏州做网站的公司哪家好制作二维码的软件
  • 广州网站建设如何做网页制作模板甜品蛋糕