I am using an activity indicator to show to the user while the code goes away and calls to azure.
The call itself works fine and all is working well but the activity indicator loads for a set period of time then afterwards the same delay that I'm trying to prevent to the user still takes place then the next screen loads.
I should probably clarify that I'm relatively new to Xamarin and a lot of this async/await stuff alludes me.
I have put the API calls in a Task and put an await method called sleep in there that runs for about 4 seconds. This was the only way I could get it to run the activity indicator. But it looks like this is also causing the problem, so to summarise I'm stuck.
I want the calls to Azure to take place while the activity indicator is going then when they return open the next page, so as to prevent page lagging and freezing. It does not look good.
So, this is the method that calls to the APIs:
private async Task HandleSubmitCommand()
{
//if (IsLoading) return;
IsLoading = true;
await Sleep();
if (!string.IsNullOrEmpty(IdentityDriver))
{
_entryFieldType = "oid";
_statusResponse = DependencyService.Get<IWebService>().Login(_entryFieldType, IdentityDriver, App.TenantId.Value.ToString());
IsLoading = false;
}
else
{
_entryFieldType = "rid";
_statusResponse = DependencyService.Get<IWebService>().Login(_entryFieldType, IdentityRoute, App.TenantId.Value.ToString());
}
if (_statusResponse == "True")
{
Application.Current.MainPage = new DriverDashboardView();
}
else
Application.Current.MainPage = new NotFoundView();
}
This is the sleep method:
private Task Sleep()
{
return Task.Delay(4000);
}
This is the Login method that calls to the API:
public string Login(string ID, string IDPayload, string TenantID)
{
//await Sleep();
var BaseURL = App.ConFigURL;
var URI = string.Format(BaseURL, TenantID);
using (var httpClient = new HttpClient(new HttpClientHandler()))
{
httpClient.BaseAddress = new Uri(URI);
var Telemetry = new { typeid = ID , id = IDPayload};
var Payload = JsonConvert.SerializeObject(Telemetry);
var SAS = DependencyService.Get<ISasToken>().CreateToken(URI, "RootManageSharedAccessKey", "#####################################");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", SAS);
var Content = new StringContent(Payload, Encoding.UTF8, "application/json");
var Response = httpClient.PostAsync(URI,Content).Result;
return Response.IsSuccessStatusCode.ToString();
}
}
As I stated the calls to Azure are fine but it doesn't seem to be running asynchronously. It doesn't help that I'm not at all comfortable with async/await.
An ideas?
I want the calls to Azure to take place while the activity indicator is going then when they return open the next page, so as to prevent page lagging and freezing.
The await Sleep(); under your HandleSubmitCommand method is unnecessary. At this point, your login operation would be executed after 4s. Based on your
Login method, you exexute the login operation synchronously via httpClient.PostAsync(URI,Content).Result. You could modify your methods as follows:
public async Task<bool> Login(string ID, string IDPayload, string TenantID)
{
.
.
var Response = await httpClient.PostAsync(URI,Content);
return Response.IsSuccessStatusCode;
}
private async Task HandleSubmitCommand()
{
IsBusy = true;
//call the login operation asynchronously
_statusResponse = await DependencyService.Get<IWebService>().Login(_entryFieldType, IdentityDriver, App.TenantId.Value.ToString());
IsBusy = false;
}
Note: You need to change the definition for your IWebService interface as follows:
public interface IWebService
{
Task<bool> Login(string ID, string IDPayload, string TenantID);
}
Related
In my Xamarin.Forms application, I have code that opens a popup page (using the Rg Popups plugin) which executes a call to my API that inserts some data on the database.
I need the async call for the popup to open to wait until the popup page's task finishes then execute another async call which updates a ListView in the current view.
However, the popup's async task does not wait until the popup has finished working to continue to the next task, which means the view isn't updated
Here's the code in question:
bool jobTask = await CloseCurOpenJob();
if (jobTask == true)
{
await FillDocuments();
}
await Navigation.PushPopupAsync(new JobClosePopupPage(Base.activeJobId));
return true;
private async Task<string> FillDocuments()
{
HttpClient client = new HttpClient();
try
{
if (jobid > 0)
{
var response = await client.GetStringAsync(myApiUri);
var DocumentsList = JsonConvert.DeserializeObject<List<DocumentHeader>>(response);
Documents = new ObservableCollection<DocumentHeader>((IEnumerable<DocumentHeader>)DocumentsList);
DocumentCount = Documents.Count();
DocumentSubtotal = Documents.Sum(instance => instance.TotalQuantity);
}
return "OK";
}
catch (Exception e)
{
return e.Message;
}
}
And here's the code in the popup page's ViewModel
private async Task<bool> CloseJob(int jobid)
{
HttpClient client = new HttpClient();
try
{
var response = await client.PostAsync(myAPIUri);
string responseString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
await CheckGetOpenJob();
return true;
}
else
{
await Application.Current.MainPage.DisplayAlert(Base.applicationTitle, responseString, "OK");
return false;
}
}
catch (Exception e)
{
await Application.Current.MainPage.DisplayAlert(Base.applicationTitle, e.Message, "OK");
return false;
}
finally
{
await Navigation.PopPopupAsync();
}
}
I have tried using Task.Run and Task.Delay but the result is still the same - the FillDocuments method runs after the popup has been shown, not after it has been dismissed.
The Task.Wait() and Task.Result will make the program wait for the async task method competed.
If the CloseCurOpenJob() is like public async Task<bool> CloseCurOpenJob(){} You can try the following code:
// Both are applicable to simple Tasks:
bool jobTask = CloseCurOpenJob().Result;
or
Task<bool> result = CloseCurOpenJob();
result.Wait();
bool jobTask = result.Result;
According to your description, you want update the popup when it show. But you sayed that the popup will not show when you used the .Wait() method.
Generally speaking, it shouldn't happen. But you can try to use the Thread.Sleep() such as:
bool jobTask = await CloseCurOpenJob();
Thread.Sleep(1000); //make the thread to wait for 1s and the async task usually completes in 1s
if (jobTask == true)
{
await FillDocuments();
}
But this way seems not elegant, you can post the code about the CloseCurOpenJob() method. In addition, if the CloseCurOpenJob() is public async bool CloseCurOpenJob(), you can make the thread wait without the await. Such as
bool jobTask = CloseCurOpenJob();
I wanted to share something I've figured out:
The problem was not in the code, but the flow of it.
Namely, I needed one page to dismiss a popup while ANOTHER page was working.
The solution I think I found - it has not been tested - is as follows:
Show the popup
Do the processing in the main page's thread after showing the popup - as it's asynchronous, the code will continue to the task.
Dismiss the popup in the main page's code (using the PopAllPopup method of the Rg plugin).
So...
bool jobTask = await CloseCurOpenJob();
if (jobTask == true)
{
await FillDocuments();
}
Would become
await Navigation.PushPopupAsync(new JobClosePopupPage(Base.activeJobId));
bool jobTask = MethodThatProcessesTheData().Result;
await Navigation.PopAllPopupAsync();
I think this is how it's supposed to go...
I'm completely new to Unity development and I'm trying to integrate an asynchronous functionality into an existing Coroutine but I have faced several issues so far.
My issues:
The Unity app completely freezes, probably because it was blocked by a thread. I implemented this code in traditional C# (console app) without having any issues.(Fixed now in Unity after some modifications)
The download task begins but it never finishes . This happens only when I run it as APK. On Unity debugging on PC works fine.
My code:
public void Validate()
{
StartCoroutine(DoWork());
}
private IEnumerator DoWork()
{
bool success;
//Perform some calculations here
..
..
success = true;
//
yield return new WaitForSeconds(3);
if(success){
GetInfo(_files);
}
}
async void GetInfo(List<string> files)
{
await StartDownload(files);
//After completing the download operation, perform some other actions in the background
...
...
//
//When done, change the active status of specific game objects
}
public async Task StartDownload(List<string> files){
var t1 = GetFileSizesAsync(files);
var t2 = DownloadFilesAsync(files);
await Task.WhenAll(t1, t2);
}
public async Task GetFileSizesAsync(List<string> urls)
{
foreach (var url in urls)
GetFileSize(url);
txtSize.text = totalSizeMb +"MB";
}
private void GetFileSize(string url)
{
var uri = new Uri(url);
var webRequest = HttpWebRequest.Create(uri);
webRequest.Method = "GET";
try
{
var webResponse = webRequest.GetResponse();
var fileSize = webResponse.Headers.Get("Content-Length");
var fileSizeInMegaByte = Math.Round(Convert.ToDouble(fileSize) / 1024.0 / 1024.0, 2);
totalSizeMb = totalSizeMb + fileSizeInMegaByte;
}
catch (Exception ex)
{
}
finally
{
webRequest.Abort();
}
}
public async Task<List<string>> DownloadFilesAsync(List<string> urls)
{
var result = new List<string>();
foreach (var url in urls)
var download = await DownloadFile(url);
if(download)
result.Add(url);
return response;
}
private async Task<bool> DownloadFile(string url)
{
WebClient webClient = new WebClient();
var uri = new Uri(url);
var saveHere = "C:\\...";
try
{
await webClient.DownloadFileTaskAsync(uri, saveHere);
return true;
}
catch (Exception ex)
{
return false;
}
}
Can you tell me what I'm doing wrong here? I've tried several ways but couldn't manage to find a proper solution.
Thank you!
I would rather make it
async Task GetInfo(List<string> files){ ... }
And then in your Coroutine do
var t = Task.Run(async () => await GetInfo(files));
while(!t.IsCompleted)
{
yield return null;
}
// Or also
//yield return new WaitUntil(() => t.IsCompleted);
if(!t.IsCompletedSuccesfully)
{
Debug.LogError("Task failed or canceled!");
yield break;
}
Note however:
When done, change the active status of specific game objects
This can't be done async! It has to happen in the Unity main thread! Therefore you would probably rather return something from your GetInfo task and activate the objects in the Coroutine when done.
after the loop and yielding you could then access the return value via
var result = t.Result;
Your web requests are currently totally redundant! You start get requests only to check how big the received content is but immediately throw away that received content ... then you start a second request to actually download the files (again).
In general I would recommend to rather use a UnityWebRequest.Get you can directly yield in the Coroutine in combination with a DownloadHandlerFile which allows you to directly download the content into a local file instead of into runtime memory.
Also
var saveHere = "C:\\...";
is hopefully not what you are trying to use as path on Android ;)
First of all.
The freezing is definitely being done by the IEneumerator as I don't see a yield statement. What's a yield statement? Uhhhh...Google it. lel
Second of all.
Wouldn't a normal void work just fine?
Third of all.
I don't know much about async's since I'm pretty new to them
but I'm fairly certain you don't need them for:
Task GetFileSizesAsync(List urls)
AND
async Task<List> DownloadFilesAsync(List urls)
I may be wrong tho.
The following Code works within a console application.
public Boolean Graph_IsMemberOfGroup(string Parm_AzureUserID, string Parm_GroupID) {
Boolean Lcl_ReturnValue = false;
Task<string> Lcl_Response = GraphPriv_IsMemberOfGroup(Parm_AzureUserID, Parm_GroupID);
if (Lcl_Response.Result != null) {
Lcl_ReturnValue = Lcl_Response.Result.Contains(Parm_GroupID);
}//end if
return (Lcl_ReturnValue);
}
private async Task<string> GraphPriv_IsMemberOfGroup(string Parm_AzureUserID, string Parm_GroupID) {
string Lcl_Returnvalue = null;
var Lcl_Uri = "https://graph.windows.net/CleMetroSchools.onmicrosoft.com/users/" + Parm_AzureUserID.Trim() + "/checkMemberGroups?api-version=1.6";
string Lcl_RequestBody = #"{""groupIds"": [""" + Parm_GroupID.Trim() + #"""]}";
Global_HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Global_Token.Result);
HttpResponseMessage Lcl_PostResult = await Global_HttpClient.PostAsync(Lcl_Uri, new StringContent(Lcl_RequestBody, Encoding.UTF8, "application/json"));
if (Lcl_PostResult.Content != null) {
Lcl_Returnvalue = await Lcl_PostResult.Content.ReadAsStringAsync();
}//end if
return Lcl_Returnvalue;
}
The call I use is
if (Graph_IsMemberOfGroup(CurrentUser,Group)){
The problem I am having is that when I Use the same code in a plain (not MVC) ASP.net web application. The system does not wait for GraphPriv_IsMemberOfGroup to completed before it tries to process the if (Lcl_Response.Result != null) {
Anything I have tried so far with waits either will not compile or waits forever.
I have been searching for several days and all I have managed to do is travel deeper down the rabbit hole of confusion.
You're misapplying the async/await model here. You shouldn't be looking for a Task<string>, you should be looking for string from an awaited method:
public async Task<Boolean> Graph_IsMemberOfGroup(string Parm_AzureUserID, string Parm_GroupID) {
Boolean Lcl_ReturnValue = false;
string Lcl_Response = await GraphPriv_IsMemberOfGroup(Parm_AzureUserID, Parm_GroupID);
return Lcl_Response.Result.Contains(Parm_GroupID);
}
An async method returns a value wrapped in a Task<>, The await keyword tells the code to wait for the method to return and unwrap the response. So if an async method returns Task<string> then you would call that method using string s = await method().
I'm really looking for the solution and can't find proper instruction.
I have async method in RestService.cs
public async static Task<List<Convert>> CheckBTCUSDAsync()
{
HttpClient client = new HttpClient();
string restUrl =
"https://bitbay.net/API/Public/BTCUSD/trades.json";
HttpResponseMessage responseGet = await
client.GetAsync(restUrl);
if (responseGet.IsSuccessStatusCode)
{
var response = await responseGet.Content.ReadAsStringAsync();
List<Convert> currencies = Convert.FromJson(response);
//Debug.WriteLine(currencies[0].Date);
return currencies;
}
else
{
//Debug.WriteLine("***************");
//Debug.WriteLine("*****FALSE*****");
//Debug.WriteLine("***************");
return null;
}
}
I want to use it in my MainPage but of course I cant use await in sync method. I found that some devs suggest putting async tasks in eg OnStart method: https://xamarinhelp.com/xamarin-forms-async-task-startup/
I need to to Bind the returned list to picker in Xaml but of course when trying to use:
var convert = RestService.CheckBTCUSDAsync().Result;
It hangs the UI thread. Anyone knows what is the best/easiest way to resolve this?
This is how I got it to work on my app
var convert = Task.Run(() => RestService.CheckBTCUSDAsync()).Result;
I must synchronize data coming from WCF WebServices in a SQLite database.
This synchronization represents a dozen of WebServices, that can be "grouped" in 4 categories:
"User's" Rights
"Forms" data, which can be updated from the two sides (user/server)
"Server" data, which are only updated from the server
"Views" data, wich copy locally the views of the server
Each call to the WebService is done through HttpClient:
response = await client.PostAsync(webServiceName, content);
Each WebService has its own async method where the WebService response is deserialized:
public static async Task<string> PushForm(List<KeyValuePair<string, string>> parameters)
{
var response = await JsonParser.GetJsonFromUrl(WebServiceName.PushForm.Value, parameters);
Forms forms = new Forms();
try
{
forms = JsonConvert.DeserializeObject<Forms>(response);
return response;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
Then I have a SynchronizationService class, where I regroup the calls to WebServices by categories:
public async Task<bool> SynchronizeServerData()
{
bool result = false;
try
{
result = true;
List<Table1> tables1 = await WebServices.GetListTable1(null);
if (tables1 != null)
{
ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table1>(true);
ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table1>(tables1);
}
List<Table2> tables2 = await WebServices.GetListTable2(null);
if (tables2 != null)
{
ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table2>(true);
ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table2>(tables2);
}
List<Table3> tables3 = await WebServices.GetListTable3(null);
if (tables3 != null)
{
ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table3>(true);
ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table3>(tables3);
}
...
}
catch (Exception e)
{
result = false;
}
return result;
}
And finally, in the main ViewModel, I call each of these methods:
public async void SynchronizeData(bool firstSync)
{
IsBusy = true;
var resUsers = await _synchronisation.SynchronizeUsersRights();
var resServer = await _synchronisation.SynchronizeServerData();
var resForm = await _synchronisation.SynchronizeForms();
var resViews = await _synchronisation.SynchronizeViews();
IsBusy = false;
}
But due to the use of "await" the performance is not good.
=> I would like to know if there is an easy way to "parallelize" the calls to optimize performance? Or is it possible to separate the data recovery from the SQLite update for this?
On the face of it there appear to be opportunities to run a few things concurrently, for instance in SynchronizeData you could do this instead:
{
IsBusy = true;
Task resUsersTask = _synchronisation.SynchronizeUsersRights();
Task resServerTask = _synchronisation.SynchronizeServerData();
Task resFormTask = _synchronisation.SynchronizeForms();
Task resViewsTask = _synchronisation.SynchronizeViews();
await Task.WhenAll(resUsersTask, resServerTask, resFormTask, resViewsTask);
var resUsers = resUsersTask.Result;
var resServer = resServerTask.Result;
var resForm = resFormsTask.Result;
var resViews = resViewsTask.Result;
IsBusy = false;
}
...which would allow those 4 tasks to run concurrently.
You could do the same in SynchronizeServerData; e.g.:
result = true;
Task tables1Task = WebServices.GetListTable1(null);
Task tables2Task = WebServices.GetListTable2(null);
Task tables3Task = WebServices.GetListTable3(null);
List<Table1> tables1 = await tables1Task;
// ...
List<Table2> tables2 = await tables2Task;
// ...
List<Table3> tables3 = await tables3Task;
// ...
This would allow the 3 tasks to run concurrently as well.
How much you actually gain from this might depend on things like does SQLite allow you to do multiple concurrent requests - I don't know the answer to that offhand.
Some other comments:
public async void SynchronizeData(bool firstSync)
async void is almost always incorrect, except for event handlers and rare fire-and-forget methods, and usually async Task is what you want. Search SO for any number of good answers on why.
Then lastly, what you really should do is profile the code to see where your real bottlenecks are.