Xamarin.Android using async method on background thread causes screen flash - c#

So I'm trying to create a loading/splash screen for an app that I'm creating. Basically, if the user isn't authenticated, then they shouldn't be able to access the other parts of the app. Additionally, I'd like the app to attempt to sync the necessary database objects before it loads up the main activity.
The problem is that when I call the Authenticate() method and the InitLocalStoreAsync() methods, the screen flashes (almost like an activity reload, or like the app is doing something that I don't understand that's hiding the activity) while the methods are executing. I'd like that not to happen.
I'm very new to Android App Dev and even newer to Xamarin.
I'm using modified code that comes from the Azure Mobile Services tutorial on authentication etc.
Should I be somehow executing these methods using RunOnUiThread? If so, how do I await in conjunction with RunOnUiThread? Or should I be doing this in a completely different way?
I'm very lost. I've tried to search and find tutorials to follow, but I can't seem to find the answer. Here's the code so far:
protected override async void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Activity_Splash);
// Create your application here
try{
CurrentPlatform.Init ();
// Create the Mobile Service Client instance, using the provided
// Mobile Service URL and key
client = new MobileServiceClient (applicationURL, applicationKey);
statusText = FindViewById<TextView> (Resource.Id.SplashStatusText);
ThreadPool.QueueUserWorkItem(x => Initialize());
}catch(Java.Net.MalformedURLException){
CreateAndShowDialog (new Exception ("There was an error creating the Mobile Service. Verify the URL"), "Error");
}catch(Exception e) {
CreateAndShowDialog (e, "Error");
}
}
private async void Initialize()
{
RunOnUiThread(() => statusText.Text = "Authenticating...");
await Authenticate();
RunOnUiThread (() => statusText.Text = "Syncing...");
await InitLocalStoreAsync();
MoveToMainActivity();
}
private async Task Authenticate()
{
try
{
user = await client.LoginAsync(this, MobileServiceAuthenticationProvider.Google);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private async Task InitLocalStoreAsync()
{
// new code to initialize the SQLite store
string path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), localDbFilename);
if (!File.Exists(path))
{
File.Create(path).Dispose();
}
var store = new MobileServiceSQLiteStore(path);
store.DefineTable<ToDoItem>();
// Uses the default conflict handler, which fails on conflict
// To use a different conflict handler, pass a parameter to InitializeAsync. For more details, see http://go.microsoft.com/fwlink/?LinkId=521416
await client.SyncContext.InitializeAsync(store);
}
How do I restructure this so that I don't get any screen flashes?

If you want to run an asynchronous method you have to use the Task Factory:
RunOnUiThread(() => statusText.Text = "Loading.");
Task.Run(() => AsyncWork()).ContinueWith(result => RunOnUiThread(() => statusText.Text = "Done!"));
The screen flashes i think it could be 2 things, the app crashed and is trying to recover the last activity or your are trying to update elements on the UI thread and doing processing/work too, so it might be "stutter".

Related

Issue with MessagingCenter firing multiple times Xamarin

I have looked all over for a solution to an issue. I have noticed that in my android app, every time I fire an event from <button Clicked="GPSToggle_Clicked">, for some reason it increments the number of times my methods get called. So after I compile and load this on my phone, I hit my "GPSToggle_Clicked" button, and then to stop hit that button again. On the first "stop", I'll get a single instance of the below output:
---------------------------------------------------------------Attempting string parsing
---------------------------------------------------------------Sending string to SubmitGeneratedGPX
---------------------------------------------------------------path: /storage/emulated/0/Download/GPX/2022-10-27-02-44-06.gpx
---------------------------------------------------------------GPX File creation success
---------------------------------------------------------------:RawBufferToJsonString: [{"Timestamp":"2022-10-27T18:43:52.849+00:00","Latitude":41.5263818,"Longitude":-81.6507923,"Altitude":153.29998779296875,"Accuracy":20.0,"VerticalAccuracy":1.7990270853042603,"Speed":null,"Course":null,"IsFromMockProvider":false,"AltitudeReferenceSystem":2},{"Timestamp":"2022-10-27T18:43:53.696+00:00","Latitude":41.5263819,"Longitude":-81.6507921,"Altitude":153.29998779296875,"Accuracy":20.0,"VerticalAccuracy":1.7697961330413818,"Speed":null,"Course":null,"IsFromMockProvider":false,"AltitudeReferenceSystem":2},{"Timestamp":"2022-10-27T18:43:54.526+00:00","Latitude":41.5263819,"Longitude":-81.6507921,"Altitude":153.29998779296875,"Accuracy":20.0,"VerticalAccuracy":1.7697961330413818,"Speed":null,"Course":null,"IsFromMockProvider":false,"AltitudeReferenceSystem":2},{"Timestamp":"2022-10-27T18:43:55.374+00:00","Latitude":41.5263819,"Longitude":-81.6507921,"Altitude":153.29998779296875,"Accuracy":20.0,"VerticalAccuracy":1.7697961330413818,"Speed":null,"Course":null,"IsFromMockProvider":false,"AltitudeReferenceSystem":2},{"Timestamp":"2022-10-27T18:43:56.21+00:00","Latitude":41.5263811,"Longitude":-81.650792,"Altitude":153.29998779296875,"Accuracy":20.0,"VerticalAccuracy":1.7160584926605225,"Speed":null,"Course":null,"IsFromMockProvider":false,"AltitudeReferenceSystem":2}]
Every subsequent time I hit start/stop on the app, I get the real-time data in the output multiplied by the number of times I've started/stopped since the last compiling.
the main app page button event thats fired:
private async void GPSToggle_Clicked(object sender, EventArgs e)
{
var LocationPermissionStatus = await Xamarin.Essentials.Permissions.RequestAsync<Xamarin.Essentials.Permissions.LocationAlways>();
var FileReadPermissionStatus = await Xamarin.Essentials.Permissions.RequestAsync<Xamarin.Essentials.Permissions.StorageRead>();
var FileWritePermissionStatus = await Xamarin.Essentials.Permissions.RequestAsync<Xamarin.Essentials.Permissions.StorageWrite>();
if(LocationPermissionStatus == Xamarin.Essentials.PermissionStatus.Denied)
{
// TODO
return;
}
// run if device is android
if(Device.RuntimePlatform == Device.Android)
{
if (!CrossGeolocator.Current.IsGeolocationAvailable || !CrossGeolocator.Current.IsGeolocationEnabled)
{
// gps is not enabled, throw alert
Console.WriteLine("---------------------------------------------------------------GPS is DISABLED");
await DisplayAlert("Error", "GPS is not enabled. You must enable GPS to use this feature", "Ok");
}
else
{
// set our IsTracking = true flag
if (!IsTracking)
{
// start background listening for GPS
await StartListening();
Console.WriteLine("---------------------------------------------------------------Listening: " + CrossGeolocator.Current.IsListening);
StartService();
Console.WriteLine("---------------------------------------------------------------Service initiated");
IsTracking = true;
Console.WriteLine("---------------------------------------------------------------Tracking initiated");
GPSToggle.Text = "Stop Tracking";
}
else
{
//
// verify that the submittal wasn't done in error, before stopping services and submitting data
bool DoneInError = await DisplayAlert("Finish?", "Are you sure you want to stop services and submit?", "No", "Yes");
if (!DoneInError)
{
await StopListening();
Console.WriteLine("---------------------------------------------------------------listening:" + CrossGeolocator.Current.IsListening);
IsTracking = false;
Console.WriteLine("---------------------------------------------------------------Tracking ceased");
// stop the gps service
StopService();
Console.WriteLine("---------------------------------------------------------------Service ceased");
// stop the background listening for gps
Console.WriteLine("---------------------------------------------------------------Attempt GPX parse from buffer obj");
GPSToggle.Text = "Start Tracking";
}
}
}
}
}
Specifically the line:
StartService();
Fires this method off within the same class, specifically the MessagingCenter.Send<>, which initiates my foreground service to handle logging the gps data into a buffer:
private void StartService()
{
var startServiceMessage = new StartServiceMessage();
MessagingCenter.Send(startServiceMessage, "ServiceStarted");
Preferences.Set("LocationServiceRunning", true);
StatusLabel.Text = "Location service has been started";
Console.WriteLine("---------------------------------------------------------------location service has been started. preferences saved");
}
and
StopService();
Fires this method off to stop the services and retrieve the gps buffer data from the foreground to the main thread:
private void StopService()
{
var stopServiceMessage = new StopServiceMessage();
MessagingCenter.Unsubscribe<App, List<Location>>(this, "GPXBufferData");
MessagingCenter.Subscribe<App, List<Location>>(this, "GPXBufferData", (sender, args) =>
{
RawGPXData = args;
Generate_CreateGPX_File(RawGPXData);
RawBufferToJsonString = GPXParse.GenerateJSON_GPXPoints(RawGPXData);
Console.WriteLine("---------------------------------------------------------------:RawBufferToJsonString: " + RawBufferToJsonString);
PromptForSubmission_GPXPoints_API();
});
Console.WriteLine("--------------------------------------------------------------------------");
MessagingCenter.Send(stopServiceMessage, "ServiceStopped");
Preferences.Set("LocationServiceRunning", false);
Console.WriteLine("---------------------------------------------------------------Location service stopped. preferences saved");
}
In the above snippet, this line is subscribed to in the GPSLocationService.cs file:
MessagingCenter.Send(stopServiceMessage, "ServiceStopped");
This is a portion of my GPSLocationService.cs file that is relevant to this:
public async Task Run(CancellationToken token)
{
int ObjCount = 0;
await Task.Run(async () => {
// if the task was stopped
// check the buffer for data, if data, send to GPXGenerator
MessagingCenter.Subscribe<StopServiceMessage>(this, "ServiceStopped",
message =>
{
if (GPSBufferObj != null)
{
Device.BeginInvokeOnMainThread(() =>
{
MessagingCenter.Unsubscribe<App, List<Location>>((App)Xamarin.Forms.Application.Current, "GPXBufferData");
MessagingCenter.Send<App, List<Location>>((App)Xamarin.Forms.Application.Current, "GPXBufferData", GPSBufferObj);
});
}
});
return;
}, token);
}
I believe I have tracked down where the issue is starting. In my StopService() method, I have the following line (just to keep track of where Im at in the buffer) and it is only sent to output once.
Console.WriteLine("--------------------------------------------------------------------------");
BUT if I place that same line within the pasted portion of my GPSLocationService.cs file, I will get the incremented output. I'm leaning towards the nested task being the issue, I wrote this based losely off of this example repro:
https://github.com/jfversluis/XFBackgroundLocationSample
You don't have MessagingCenter.Unsubscribe<StopServiceMessage> anywhere in your code. StopServiceMessage is what you are accumulating subscriptions to.
You need to make sure Unsubscribe is unsubscribing the instance that was previously subscribed to. It sounds to me like there are multiple instances of GPSLocationService. [In which case, this is no longer referring to the original instance. Unsubscribe won't do anything, unless you have the this that was originally Subscribed.]
If so, instead create an instance of GPSLocationService ONCE, and store it in a static variable. Re-use it. start/stop it, but don't discard it.
Alternatively, if you only want a message ONE TIME from each Subscribe, then Unsubscribe as soon as you receive each message:
MessagingCenter.Subscribe<StopServiceMessage>(this, "ServiceStopped",
message =>
{
MessagingCenter.Unsubscribe<StopServiceMessage>(this, "ServiceStopped");
... your code ...
});
Use this same pattern EVERYWHERE you have a Subscribe (unless you Subscribe ONLY ONE TIME at app start, as Jason suggested.)

WPF Application does not seem to be receiving a response

I currently have two separate projects, one is a WPF application with .NET Framework 4.7.2 and the other is a console application with ASP.NET Core 3.1. The console application used to be .NET 4.7.2 as well however I have just finished moving it to Core.
The WPF Application sends an object to the console application and the console application does some stuff with it and returns a response. My issue currently is that the WPF application successfully sends the object, the console application receives it and does what it needs, however when it sends the response, the WPF application just hangs and must be completely stopped. Here is the code for both:
WPF Application (was working perfectly fine before moving the console to core):
static async Task SendRequest(UploadDetails u)
{
System.Diagnostics.Debug.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(u));
HttpResponseMessage response = await _client.PostAsJsonAsync(_clientUrl, u);
if (response.IsSuccessStatusCode)
{
var responseLink = await response.Content.ReadAsStringAsync();
responseLink = responseLink.Replace("\"", "");
System.Diagnostics.Debug.WriteLine("SUCCESSFUL UPLOAD"); //TODO: Add response
Messenger.Default.Send(new NotificationMessage(responseLink));
}
else
{
System.Diagnostics.Debug.WriteLine("UNSUCCESSFUL UPLOAD"); //TODO: Add response
}
}
Console Application (even sends correct response with postman):
[HttpPost("upload")]
public ActionResult Upload(UploadDetails data)
{
try
{
Task.Factory.StartNew(() => { _Uploader.Upload(data); });
return Ok("Started");
}
catch (Exception e)
{
return NotFound();
}
}
Any help would be greatly appreciated.
Without further information it is hard to determine the actual problem....
But just by looking at the code provided I see one problem:
You are sending JSON yet your action is not inspecting the body.
So I would make one change:
1 Your parameter should have an attribute [FromBody] like this:
[HttpPost]
public ActionResult Upload([FromBody] UploadDetails data)
Also you have to make sure your HttpClient has the correct headers when initialized like this:
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
The last thing I can think of is that you are calling your async method wrong in the WPF application....
For example this would hang on the "await _client.PostAsJsonAsync....." line
private void Button_Click(object sender, RoutedEventArgs e)
{
SendRequest(new UploadDetails { }).Wait();
}
This would NOT hang:
private async void Button_Click(object sender, RoutedEventArgs e)
{
await SendRequest(new UploadDetails { });
}
The reason is that the first one would block the main thread and the second one would not.
But without the code that calls the SendRquest it is impossible to know.

Validate internet connection or Azure service - Xamarin

I have a application develop in Xamarin (Cross Platform) that now run great!! But always need internet connection.
If internet connection will fail, my app suffers an unexpected shutdown. I would like controle this.
First I have "AzureDataService" class:
public class AzureDataService
{
//Conexion to backend
public MobileServiceClient MobileService { get; set; }
//Object of "Ficha" class.
IMobileServiceSyncTable<Ficha> tablaFicha;
public async Task Initialize()
{
if (isInitialized)
return;
MobileService = new MobileServiceClient("http://linkbdd");
//Save data in a local DB, later upload with internet connection
const string path = "bbddMuestra.db";
var store = new MobileServiceSQLiteStore(path);//Create DB
store.DefineTable<Ficha>();
//async initialization
await MobileService.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler());
//Inicializate table
tablaFicha = MobileService.GetSyncTable<Ficha>();
isInitialized = true;
}
//Here get data
public async Task<IEnumerable<Ficha>> GetData()
{
await Initialize();
await SyncFicha();
//Select data...
return await tablaFicha.OrderBy(a => a.Id).ToEnumerableAsync();
}
public async Task SyncFicha()
{
await tablaVehiculos.PullAsync("Ficha", tablaFicha.CreateQuery());
await MobileService.SyncContext.PushAsync();
}
End of "AzureDataService" class. Now the class when implement AzureDataService.
public partial class ListaFichas : ContentPage
{
public static ObservableCollection ficha;
public ListaFichas ()
{
InitializeComponent ();
ficha = new ObservableCollection();
}
protected async override void OnAppearing()
{
base.OnAppearing();
ficha.Clear();
//Next line get data from previous method of class "AzureDataService"
var ligas = await App.AzureService.GetData();
foreach(var item in ligas)
{
Ficha fi = item;
ficha.Add(fi);
}
//Here bind listview with data that previous get
lsvFichas.ItemsSource = ficha;
}
Please help me. I would like show a Display or DisplayActionSheet to inform user...But never unexpected shutdown.
Thanks!...
You have to check internet connection
Below link is useful for you
https://forums.xamarin.com/discussion/comment/276931#Comment_276931
AFAIK, if you call PushAsync to PUSH the sync content, then the list of creates, updates and Deletes against your offline tables would be sent one by one to the Azure App Service. Both PullAsync and PushAsync operations need your mobile client online.
Per my understanding, you could check the internet connection before call SyncFicha for sync your data. Also, you could just wrap your SyncFicha method with try-catch for both handling the internet connection and conflict when you pushing the offline data. I recommend that you could refer to the following tutorials from adrian hall's book as follows:
Detecting Connection State
Use Xam.Plugin.Connectivity for checking connectivity state as follows:
await Initialize();
if (!(await CrossConnectivity.Current.IsRemoteReachable(Client.MobileAppUri.Host, 443)))
{
Debug.WriteLine($"Cannot connect to {Client.MobileAppUri} right now - offline");
return;
}
await tablaVehiculos.PullAsync("Ficha", tablaFicha.CreateQuery());
await MobileService.SyncContext.PushAsync();
Handling Conflict Resolution

How to Request Location permission before showing map set up in Xaml. Xamarin.Forms

I recently updated sdk level to 6.0 in Xamarin.forms.
I used Xaml to place a map on a page. Since I updated to 6.0 permission is required to show the map. My problem now is I can't figure out how to request permission to show the map before the app attempts to show it. As a result I get an unhandled exception.
public MapPage()
{
Init();
InitializeComponent();
azureService = AzureService.defaultManager;
}
private async Task Init()
{
await RequestLocationPermission();
}
protected async override void OnAppearing()
{
base.OnAppearing();
MyMap.MoveToRegion(
MapSpan.FromCenterAndRadius(
new Position(0, 0),
Distance.FromMiles(10.0)));
}
private async Task RequestLocationPermission()
{
try
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Location);
if (status != PermissionStatus.Granted)
{
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Location))
{
await DisplayAlert("Need location", "Gunna need that location", "OK");
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Permission.Location });
status = results[Permission.Location];
}
if (status == PermissionStatus.Granted)
{
}
else if (status != PermissionStatus.Unknown)
{
await DisplayAlert("Location Denied", "Can not continue, try again.", "OK");
}
}
catch (Exception ex)
{
}
}
If the map is set up in Xaml, how can I request permission before showing it?
The way your constructor is set up right now, you are starting the permission request in a task which will run in a separate thread. That means that InitializeComponent() will probably run before the user can grant permission. The problem is you can't make the constructor an async method so there isn't an easy way to get around this.
To make this work without to much effort, you can move the InitializeComponent() from your constructor into your "if (status == PermissionStatus.Granted)" block. It would probably look something like this:
if (status == PermissionStatus.Granted)
{
Device.BeginInvokeOnMainThread(() =>
{
InitializeComponent()
});
}
With this approach you will have to be careful what you do in OnAppearing() as it will probably be called before InitializeComponent(). If you try to access any of your UI components at that point it will fail.
However I think the better way to handle this is to move your permission request code one level up. In other words put it in the class where you are instantiating this page from. Then you can show this page if access is granted, or another page that does not have the map if access is denied. It would make for a better user experience.

WinRT C#: Cannot save UnhandledException to Storage

I'm working on WinRT. If an unhandled exception is thrown I want to write the message text to the storage.
I added an Event handler in 'App.xaml.cs', see the code.
The exception is caught but the last line, where the file is written, crashes again -> 'exception'!
Why? Any idea?
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
this.UnhandledException += App_UnhandledException;
}
async void App_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile file= await folder.CreateFileAsync("crash.log",CreationCollisionOption.OpenIfExists);
await FileIO.AppendTextAsync(file, e.Message); // <----- crash again -----
}
Thanks
Sunny
I've been wondering the same thing and stumbled across this quite early on in my search. I've figured out a way, hopefully this will prove useful to someone else too.
The problem is that await is returning control of the UI thread and the app's crashing. You need a deferral but there's no real way to get one.
My solution is to use the settings storage, instead. I'm assuming most people wanting to do this want to do something LittleWatson style, so here's some code modified from http://blogs.msdn.com/b/andypennell/archive/2010/11/01/error-reporting-on-windows-phone-7.aspx for your convenience:
namespace YourApp
{
using Windows.Storage;
using Windows.UI.Popups;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
public class LittleWatson
{
private const string settingname = "LittleWatsonDetails";
private const string email = "mailto:?to=you#example.com&subject=YourApp auto-generated problem report&body=";
private const string extra = "extra", message = "message", stacktrace = "stacktrace";
internal static void ReportException(Exception ex, string extraData)
{
ApplicationData.Current.LocalSettings.CreateContainer(settingname, Windows.Storage.ApplicationDataCreateDisposition.Always);
var exceptionValues = ApplicationData.Current.LocalSettings.Containers[settingname].Values;
exceptionValues[extra] = extraData;
exceptionValues[message] = ex.Message;
exceptionValues[stacktrace] = ex.StackTrace;
}
internal async static Task CheckForPreviousException()
{
var container = ApplicationData.Current.LocalSettings.Containers;
try
{
var exceptionValues = container[settingname].Values;
string extraData = exceptionValues[extra] as string;
string messageData = exceptionValues[message] as string;
string stacktraceData = exceptionValues[stacktrace] as string;
var sb = new StringBuilder();
sb.AppendLine(extraData);
sb.AppendLine(messageData);
sb.AppendLine(stacktraceData);
string contents = sb.ToString();
SafeDeleteLog();
if (stacktraceData != null && stacktraceData.Length > 0)
{
var dialog = new MessageDialog("A problem occured the last time you ran this application. Would you like to report it so that we can fix the error?", "Error Report")
{
CancelCommandIndex = 1,
DefaultCommandIndex = 0
};
dialog.Commands.Add(new UICommand("Send", async delegate
{
var mailToSend = email.ToString();
mailToSend += contents;
var mailto = new Uri(mailToSend);
await Windows.System.Launcher.LaunchUriAsync(mailto);
}));
dialog.Commands.Add(new UICommand("Cancel"));
await dialog.ShowAsync();
}
}
catch (KeyNotFoundException)
{
// KeyNotFoundException will fire if we've not ever had crash data. No worries!
}
}
private static void SafeDeleteLog()
{
ApplicationData.Current.LocalSettings.CreateContainer(settingname, Windows.Storage.ApplicationDataCreateDisposition.Always);
var exceptionValues = ApplicationData.Current.LocalSettings.Containers[settingname].Values;
exceptionValues[extra] = string.Empty;
exceptionValues[message] = string.Empty;
exceptionValues[stacktrace] = string.Empty;
}
}
}
To implement it, you need to do the same as the link above says, but to ensure the data's here in case the url ever goes down:
App.xaml.cs Constructor (BEFORE the call to this.InitializeComponent()):
this.UnhandledException += (s, e) => LittleWatson.ReportException(e.Exception, "extra message goes here");
Obviously if you already have an UnhandledException method you can throw the call to LittleWatson in there.
If you're on Windows 8.1, you can add a NavigationFailed call too. This needs to be in an actual page (typically MainPage.xaml.cs or whatever page is first opened):
xx.xaml.cs Constructor (any given page):
rootFrame.NavigationFailed += (s, e) => LittleWatson.ReportException(e.Exception, "extra message goes here");
Lastly, you need to ask the user if they want to send the e-mail when the app re-opens. In your app's default Page's constructor (default: the page App.xaml.cs initializes):
this.Loaded += async (s, e) => await LittleWatson.CheckForPreviousException();
Or add the call to your OnLoad method if you already use it.
In this situation, await could be loosely translated to "do this job on another thread, and continue what you were doing while you wait for it to finish". Given that what your app was doing was crashing, you probably don't want it to continue doing that until you're done logging the problem. I'd suggest running your file IO synchronously in this case.
This may come a bit too late for the original question but...
as #Hans Passant suggested, avoiding await (i.e., running the FileIO.AppendTextAsync() synchronously), also seconded by #Jon, I would opt for this rather than the relatively too heavy code for LittleWatson. As the app is in some error handing state anyway (this should be a rare occurrence) I wouldn't put any blocking arising from synchronous (due to removing await) as a major downside.
Leaving the synchronous option to one side, the following await implementation worked for me:
Change await FileIO.AppendTextAsync(file, e.Message); to:
Task task = LogErrorMessage(file, e.Message)
task.Wait(2000); // adjust the ms value as appropriate
...
private async Task LogErrorMessage(StorageFile file, string errorMessage)
{
await FileIO.AppendTextAsync(file, errorMessage); // this shouldn't crash in App_UnhandledException as it did before
}

Categories

Resources