How to timeout Firebase Task in Unity - firebase

This issue is driving me nuts :)
Assuming that I have a simple async Task:
async Task AddPoints()
{
await usersDbReference.Child(currentUser).Child("Score").SetValueAsync(newScore).ContinueWith(task =>
{
if(task.IsFaulted || task.IsCanceled)
{
Debug.Log("Couldn't complete task");
}
});
}
What is the simplest way to add the timeout, for example 10 seconds, after which I will show pop up to the user to check his/her internet connection?
Thank you in advance!
EDIT:
I tried below code but it makes the unity crash:
int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
} else {
// timeout logic
}
I'm using Unity (api compatibility level .Net Standard 2.0, I assume that crash is because of this?)
What I want to achieve:
In case the user doesn't have the internet connection I want to either timeout the task and cancel it or just cancel it after finding out there is no internet connection.
EDIT:
I modified the code. So I have a simple task with cancel token:
async Task CheckTask(CancellationToken csToken)
{
string firstChild = "user";
string secondChild = "testuser";
await FirebaseDatabase.DefaultInstance.RootReference.Child(firstChild).Child(secondChild).GetValueAsync().ContinueWith(task =>
{
if(task.IsFaulted || task.IsCanceled)
{
Debug.Log("Task was canceled");
}
});
}
Then I'm calling it from async void:
public async void FirebaseLogin()
{
bool taskDidntComplete = false;
Debug.Log("Started task");
CancellationTokenSource cs = new CancellationTokenSource();
try
{
var loginTask = CheckTask(cs.Token);
if(loginTask.IsCanceled)
{
Debug.Log("Canceled");
}
if (await Task.WhenAny(loginTask, Task.Delay(10000)) == loginTask)
{
taskDidntComplete = false;
}
else
{
taskDidntComplete = true;
Debug.Log(taskDidntComplete);
cs.Cancel();
throw new TimeoutException();
}
}
catch (Exception e)
{
Debug.Log(e);
}
finally
{
}
}
And while everything works fine and it shows the exception, it doesn't cancel the task. Would be very grateful if someone could tell me what I'm doing wrong.
EDIT2: Works perfect in Unity, doesnt work on Android... Anyone can help? I'm desperate now haha
public async void FirebaseLogin()
{
Debug.Log("Started task");
CancellationTokenSource cs = new CancellationTokenSource();
try
{
var loginTask = CheckTask(cs.Token);
if(loginTask.IsCanceled)
{
Debug.Log("Canceled");
netTestCheck.text = "Canceled";
}
if (await Task.WhenAny(loginTask, Task.Delay(10000)) == loginTask)
{
//netTestCheck.text = "Completed";
}
else
{
netTestCheck.text = "Failed";
cs.Cancel(false);
//throw new TimeoutException();
}
cs.Token.ThrowIfCancellationRequested();
}
catch (Exception e)
{
netTestCheck.text = "Failed2";
Debug.Log(e);
}
finally
{
}

It would help if you could share the exception or stack trace of the crash (integrating Crashlytics could help if you're already in the Firebase ecosystem).
Although I can't spy anything that looks particularly bad in your sample code, if the actual Task fails for whatever reason (say you turn on airplane mode to test, and a suitable exception is raised before your timeout) an exception will get raised there that you aren't handling.
I'd suggest putting a try/catch around your block like:
try {
int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
} else {
// timeout logic
}
} catch (Exception e) {
Debug.LogError($"{e} occurred!");
}
It's likely to be a DatabaseException, but I'd check first before you get more specific than Exception.
Let me know if that helps!
--Patrick

It unfortunately won't work on Android because app will keep on calling to Firebase. Fortunately found the way around :D

Related

Method in event handlers are not called in SignalR after some time

On starting, the application with SignalR 2 implementation works as expected. But after sometime (it can be after 5 minutes or 30 minutes or 10 minutes), the events are not called anymore. But the Server and Client are still connected. I know it is connected because it the trace level of the client for SignalR, it can still get the commands coming from the Server.
But my messages from my client is still being received by the server and its action executed so I know that the Server - Client connection is still good.
Also, I know that it is still connected because I always check the status of the connection.
This is how I implement the SignalR
public void ConnectAsync()
{
WriteToLogs("Connecting to Server...");
var dic = new Dictionary<string, string> { { "UserName", _signalrSettings.ApplicationName } };
_hubConnection?.Dispose();
_hubConnection = new HubConnection(_signalrSettings.ServerUri, dic);
_hubConnection.StateChanged += Connection_StateChanged;
_hubProxy = _hubConnection.CreateHubProxy(_signalrSettings.HubProxy);
try
{
_hubProxy.On<string, string>("Initial", (a, b) => Initial());
_hubConnection.Start().ContinueWith((s) =>
{
if (s.IsCompleted)
{
_isCompleted = true;
}
else if (s.IsCanceled)
{
_isCompleted = false;
}
else if (s.IsFaulted)
{
_isCompleted = false;
}
_hubProxy.On<string, string>("NewMessage", (userName, message) => NewMessage(userName, message)); // THE "NewMessage" is not fired after some time.
WriteToLogs( $#"Connection status is {_hubConnection.State}");
}).Wait();
}
catch (Exception e)
{
var trace = new StackTrace(e, true);
var reflectedType = trace.GetFrame(0).GetMethod().ReflectedType;
if (reflectedType != null)
{
_messageOperation.WriteErrors($#"Error {e.StackTrace} at {trace}");
}
}
}

Getting an exception Android.Util.AndroidRuntimeException: Only the original thread that created a view hierarchy can touch its views

Here is the Code and where i am sending request to fetching the data and first time it is loading fast and when i am coming back from anuyother page then this page is taking too much time that too when i am using Device.BeginInvokeOnMainThread() within Task.Run().
==============================================================================
public Home()
{
if (HomeModel.GetInstance().GetHomeDataPopulate())
{
FireContentPageInitialized();
}
else
{
CallRequest();
}
}
private async void CallRequest()
{
await Task.Run(async () =>
{
await Task.Delay(3000);
SetBannerData();
SetBrandsData();
SetHotDealsData();
SetNewlyAddedData();
SetRecentlyViewed();
SetRecentlyPurchased();
});
}
//Sending Requests to fetching data in subscribed events...
private void SetBannerData()
{
ModelController.OnGetHomeBannerDataSuccess += ModelController_OnGetHomeBannerDataSuccess;
ModelController.OnGetHomeBannerDataFailure += ModelController_OnGetHomeBannerDataFailure;
ModelController.FetchBannerData();
}
//Fetching Data and binding with UI Itemsource...
private void ModelController_OnGetHomeBannerDataSuccess(object sender, CustomEventArgs eventArgs)
{
stackBanner.ItemsSource = null;
stackBanner.ItemsSource = HomeModel.GetInstance().GetHomeBanners();
}
==============================================================================
This is my Code..and i added comment line where i am binding UI. I can't add lots of code here because it's too lengthy so i have added only one method which is binding UI and other methods are also doing the same.
You are trying to update view by another thread. To update view created by main thread from another thread you must do something like this. Example pseudo code (I don't know your methods).
await Task.Run(() => {
try
{
Device.BeginInvokeOnMainThread(() =>
{
SetBannerData();
SetBrandsData();
SetHotDealsData();
SetNewlyAddedData();
SetRecentlyViewed();
SetRecentlyPurchased();
}
}
catch (Exception ex)
{
}

async method cannot return void - How can I handle this?

I have a phone application. When a screen displays I start a timer like this:
base.OnAppearing();
{
timerRunning = true;
Device.BeginInvokeOnMainThread(() => showGridTime(5));
}
async void showGridTime(int time)
{
while (timerRunning)
{
var tokenSource = new CancellationTokenSource();
await Task.Delay(time, tokenSource.Token);
detailGrid.IsVisible = true;
}
}
The code seems to work but there is a warning message in the IDE saying that an async method cannot return null.
Given this code can someone help and give me advice on what I should return and if I am going about this in the correct way?
Just return a task:
async Task ShowGridTimeAsync(int time)
{
while (timerRunning)
{
var tokenSource = new CancellationTokenSource();
await Task.Delay(time, tokenSource.Token);
detailGrid.IsVisible = true;
}
}
This is necessary to have the caller of this method know when it is completed and act accordingly.
It is not recommended to create async void methods unless you're creating an event or forced to do that in order to meet an interface signature.

Netty ServerBootstrap - asynchronous binding?

First, here's a reference to where I read all of what I know now regarding this question: http://docs.jboss.org/netty/3.2/api/org/jboss/netty/bootstrap/ServerBootstrap.html#bind%28%29
Although not explicitly specified by the documentation, it would seem that ServerBootstrap.bind is synchronous - because it does not return a ChannelFuture, but rather a Channel. If that is the case, then I do not see any way to make an asynchronous bind using the ServerBootstrap class. Am I missing something or will I have to roll my own solution?
Best regards
I ended up rolling my own bootstrap implementation with the following addition:
public ChannelFuture bindAsync(final SocketAddress localAddress)
{
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
final BlockingQueue<ChannelFuture> futureQueue =
new LinkedBlockingQueue<ChannelFuture>();
ChannelHandler binder = new Binder(localAddress, futureQueue);
ChannelHandler parentHandler = getParentHandler();
ChannelPipeline bossPipeline = pipeline();
bossPipeline.addLast("binder", binder);
if (parentHandler != null) {
bossPipeline.addLast("userHandler", parentHandler);
}
getFactory().newChannel(bossPipeline);
ChannelFuture future = null;
boolean interrupted = false;
do {
try {
future = futureQueue.poll(Integer.MAX_VALUE, TimeUnit.SECONDS);
} catch (InterruptedException e) {
interrupted = true;
}
} while (future == null);
if (interrupted) {
Thread.currentThread().interrupt();
}
return future;
}
In Netty 3.6 there is an async bind. Here's the javadoc: http://netty.io/3.6/api/org/jboss/netty/bootstrap/ServerBootstrap.html#bindAsync()

interesting service behaviour in silverlight

I have a Silverlight project which takes some encrypted string thru its Service Reference: DataService (service which is done in an ASP.NET project).
The method from TransactionServices.cs to get the encrypted string is:
public void GetEncryptedString(string original)
{
DataService.DataServiceClient dataSvc = WebServiceHelper.Create();
dataSvc.GetEncryptedStringCompleted += new EventHandler<SpendAnalyzer.DataService.GetEncryptedStringCompletedEventArgs>(dataSvc_GetEncryptedStringCompleted);
dataSvc.GetEncryptedStringAsync(original);
}
On completing, put the result in encodedString var (which is initialized with an empty value):
void dataSvc_GetEncryptedStringCompleted(object sender, SpendAnalyzer.DataService.GetEncryptedStringCompletedEventArgs e)
{
if (e.Error == null)
{
try
{
if (e.Result == null) return;
this.encodedString = e.Result;
}
catch (Exception ex)
{
Logger.Error("TransactionService.cs: dataSvc_GetEncryptedStringCompleted: {0} - {1}",
ex.Message, ex.StackTrace);
MessageBox.Show(ex.ToString());
}
}
}
Now I want to get the encoded string from my MainPage.xaml like:
TransactionService ts = new TransactionService();
ts.GetEncryptedString(url);
Console.WriteLine(ts.encodedString);
I do not uderstand why ts.encodedString is empty. When I do the debug I see that it actually prints out empty and AFTER that it goes to the void dataSvc_GetEncryptedStringCompleted to take the result and fill it.
Can you point me what I've done wrong? Is there a way to wait for the encodedString to be fetched and only after that to continue?
Thanks a lot.
When you call the ts.GetEncryptedString(url); you just started async operation. And therefor the value you are accessing is will be set only in the callback method.
But you access it before the value is modified by the callback.
The solution which I am using will looks similar to folowing:
Redefine the GetEncryptedString method signature.
public void GetEncryptedString(string original, Action callback)
{
DataService.DataServiceClient dataSvc = WebServiceHelper.Create();
dataSvc.GetEncryptedStringCompleted += (o,e) =>
{
dataSvc_GetEncryptedStringCompleted(o,e);
callback();
}
dataSvc.GetEncryptedStringAsync(original);
}
Call it like this:
ts.GetEncryptedString(url, OtherLogicDependantOnResult);
where
OtherLogicDependantOnResult is
void OtherLogicDependantOnResult()
{
//... Code
}

Resources