Description
Unity Upgrade
I've recently upgrade from Unity 2017.3.0f3 because I started getting freezes on Android when I call anything from the Firebase SDK. The version of Unity I have now is 2018.1.7f1. I am also using the latest firebase sdk 5.1.1.
.NET Upgrade and IL2CPP
Still not getting the game running on android (editor works fine), I switched to IL2CPP and .NET 4.x. As far as I can tell, these technologies are compatible with the Firebase SDK as long as use the correct Parse SDK dlls. See bottom of this page.
Editor Errors
Now, I'm getting the following error when I try run the game in the editor (Android is also still broken):
get_isActiveAndEnabled can only be called from the main thread.
The stack trace points to a simple line of code that, if I delete it, it just throws a similar error somewhere else (code that always worked prior to Firebase):
get_gameObject can only be called from the main thread.
The Issue
Finally, I tested disabling my firebase sdk. This resulted in the game running smoothly in the editor and on an Android device.
The errors also make it quite clear that Unity can't handle code called from multiple threads. I've also seen this from other issues people are experiencing. I also believe this is why the Android game on Unity 2017 didn't exactly crash, it just froze (probably switching to a different thread once Firebase SDK is used). And it only happened occasionally, not every time (perhaps there aren't always other threads available). I'm also not sure whether this issue started with Firebase Authentication or Realtime Database (I'm assuming both because they both use System.Threading).
Anyway...
- Is this a known issue people are experiencing? How did you fix it?
- Is there perhaps a older SDK/Unity version that fixes this? If so, please share your versions.
- Is there a trick to put all the Firebase SDK calls on the main thread even if it uses System.Theading.Task to run the calls asynchronously?
- Perhaps I'm using the Firebase SDK incorrectly? Here is an snippet from one of my functions:
if (auth.CurrentUser.IsAnonymous) {
Task<DataSnapshot> dbGetUser = DBGetUser();
dbGetUser.ContinueWith(tGetUser => {
if (tGetUser.IsCanceled || tGetUser.IsFaulted || !tGetUser.IsCompleted) {
return;
}
if (tGetUser.Result == null || tGetUser.Result.GetRawJsonValue() == null) {
Task dbNewUser = DBNewUser(true);
dbNewUser.ContinueWith(tNewUser => {
if (tNewUser.IsCanceled || tNewUser.IsFaulted || !tNewUser.IsCompleted) {
return;
}
Task<DataSnapshot> dbGetNewUser = DBGetUser();
dbGetNewUser.ContinueWith(tGetNewUser => {
if (tGetNewUser.IsCanceled || tGetNewUser.IsFaulted || !tGetNewUser.IsCompleted) {
return;
}
PrepareGame("Anonymous", JsonUtility.FromJson<User>(tGetNewUser.Result.GetRawJsonValue()));
});
});
} else {
PrepareGame("Anonymous", JsonUtility.FromJson<User>(tGetUser.Result.GetRawJsonValue()));
}
});
}