问题描述
使用GetValueAsync()。ContinueWith(task ..)中的回调通过Firebase恢复数据后,我想实例化预制件,以查看排行榜的得分列表。但是,它什么也不做,我没有错误。一旦遇到“ this”或“ instantiate”,代码便会立即停在回调UseSores中。
public class leaderboardmanager : MonoBehavIoUr
{
public GameObject rowLeardBoard;
FirebaseDB_Read read;
float positionX;
int nbrows = 10;
void Start()
{
read = (gameObject.AddComponent<FirebaseDB_Read>());
GetscorePlayer();
}
void GetscorePlayer()
{
read.Getscores(Usescores,"entries/leaderBoard/",nbrows);
}
void Usescores(IList<FirebaseDB_Read.score> scores)
{
Debug.Log("arrive here");
positionX = this.transform.position.y;
Debug.Log("does not arrive here");
}
}
这里是要获取我的数据:
public class FirebaseDB_Read : MonoBehavIoUr
{
public class score
{
public string UID;
public string score;
public int rank;
}
public void Getscores(Action<IList<score>> callback,string URL_TO_scoreS,int limit)
{
DatabaseReference scoresRef = FirebaseDatabase.DefaultInstance.GetReference(URL_TO_scoreS);
scoresRef.OrderByChild("score").LimitToLast(limit).GetValueAsync().ContinueWith(task =>
{
DataSnapshot snapshot = task.Result;
IList<score> objectsList = new List<score> { };
int i = 1;
foreach (var childSnapshot in snapshot.Children)
{
score score = new score();
score.rank = i;
score.UID = childSnapshot.Child("UID").GetValue(true).ToString();
score.score = childSnapshot.Child("score").GetValue(true).ToString();
objectsList.Add(score);
i++;
}
callback(objectsList);
});
}
}
解决方法
这是Unity中经常问到的问题:因为您ContinueWith
在后台线程上!
Unity不是线程安全的,这意味着大多数Unity API只能在Unity主线程中使用。
Firebase提供了一个专门针对Unity的扩展:ContinueWithOnMainThread
,可确保在访问API有效的Unity主线程中处理结果。
scoresRef.OrderByChild("score").LimitToLast(limit).GetValueAsync().ContinueWithOnMainThread(task =>
{
...
});
作为替代,您可以使用一种所谓的“主线程分配器”模式,并确保callback
在接收方的主线程中执行。这样的好处是列表上仍然昂贵的操作全部在后台线程上执行,而不会影响UI性能
scoresRef.OrderByChild("score").LimitToLast(limit).GetValueAsync().ContinueWith(task =>
{
...
});
但是在FirebaseDB_Read
private readonly ConcurrentQueue<Action> _mainThreadActions = new ConcurrentQueue<Action>();
private void Update()
{
if(_mainThreadAction.Count > 0)
{
while(_mainThreadActions.TryDequeue(out var action))
{
action?.Invoke();
}
}
}
void GetScorePlayer()
{
read.GetScores(UseScores,"entries/LeaderBoard/",nbRows);
}
void UseScores(IList<FirebaseDB_Read.Score> scores)
{
// handle this in the next main thread update
_mainThreadActions.Enqueue(() =>
{
Debug.Log("arrive here");
positionX = this.transform.position.y;
Debug.Log("does not arrive here");
}
}
当然,这反过来会增加一些开销,用于检查Update
中的任何新动作。因此,如果您计划使用多个此类后台操作,请确保在一个中央位置实施这些操作,以保持有限的开销;)