How can I halt execution until a task completes?

JKnottJKnott Member ✭✭✭

Hi there everyone, I am having a bit of a conundrum here...
I have a function in my app where a user can send an image from the app in a text message, but the images are stored in the database.
To do this I follow these steps
1. Create a file with the image,
2. Send it,
3. Delete the image.

My problem is, the order of the functions seems to be...
1. Create file.
2. Delete file.
3. Send file that isn't there....

Here is the code I am using
This is the code to start the process...

    private async void OnTapped(object sender, EventArgs e)
    {
        string action = await Application.Current.MainPage.DisplayActionSheet("Picture Options", null, "Ok", "Edit Photo", "Save to file", "Share Photo", "Email Photo", "Delete Photo");
        TapHandler.ImageTapHandler((Photos)((TappedEventArgs)e).Parameter, action, null);
    }

and here is what that code calls to

    ...

    MemoryStream stream = null;
        //Display share dialog
            if (action.Contains("Share"))
            {
                try
                {
                    if (!File.Exists($"{szFolderPath}/{IncomingImage.FileName}.jpg"))
                    {
                        stream = new MemoryStream(IncomingImage.Image);
                        Xamarin.Forms.DependencyService.Get<ISave>().SaveAndView($"{IncomingImage.FileName}.jpg", "image/jpeg", stream);
                    }
                    if (File.Exists($"{szFolderPath}/{IncomingImage.FileName}.jpg"))
                    {
                        ShareFileRequest sf = new ShareFileRequest(new ShareFile($"{szFolderPath}/{IncomingImage.FileName}.jpg"));
                        Share.RequestAsync(sf).Wait();
                    }
                }
                catch
                {
                }
                finally
                {
                    if (File.Exists($"{szFolderPath}/{IncomingImage.FileName}.jpg"))
                        File.Delete($"{szFolderPath}/{IncomingImage.FileName}.jpg");

                    if (stream != null)
                    {
                        stream.Dispose();
                    }
                }
            }

    ...

It was my impression that the Wait() at the end of the Share.RequestAsync() would block the running thread until it had completed the Task?
If this is not the case, is there a better way to go about blocking the thread until the Share function completes? Otherwise is there a way to hand it a callback to call upon completion? I've tried using .ContinueWith( t => { ...my delete code here...}); but again that just runs immediately.

From what I can tell, the Share function is basically just calling an external API and continuing along on it's own.. If this is the case, I need to implement some form of callback to run when the task completes, I guess I need to know if I can force the app to stop there and wait, or alternately how I can create a callback to cleanup the file once I am complete?

Thanks again,

Best Answer

Answers

  • JoeMankeJoeManke USMember ✭✭✭✭✭

    Why not make that code async and await Share.RequestAsync instead of using .Wait()?

    Is there an exception being thrown that you're just ignoring with that empty catch block?

  • JKnottJKnott Member ✭✭✭

    I've tried that, but the await just fires off the command to the Share.RequestAsync and passes into the finally immediately. I Have not researced the Share function much, it's a part of the extras library, so I figured it would block until the proces it began finishes. (IE the text message is sent).
    I've also added a catch (Exception) to the catch block so it doesn't seem to be catching any exceptions..
    Is there a better way to go about it?
    Should I look into making my own Garbage Collector that I hand the Share.RequestAsync task and pointer to the file to?

  • JKnottJKnott Member ✭✭✭

    Sorry, in my above comment I meant to say the Xamarin.Essentials lib...

    I've changed some of my code around hoping that it was perhaps just something simple, or indeed there was an exception that I was not catching, but I have confirmed that it is indeed not any of those...

    This code for example, works just fine. It sends a text message with the image attached.

        //Something went wrong, and there is no action to take
                if (IncomingImage == null || string.IsNullOrEmpty(action))
                    return;
    
                string szPath = $"{DependencyService.Get<ISave>().GetSavePath()}/{IncomingImage.FileName}.jpg";
                PhotosRepository PhotoRepo = new PhotosRepository();
    
                Debug.WriteLine(string.Format("Action: {0} On {1}", action, IncomingImage.Path));
    
                //Display share dialog
                if (action.Contains("Share"))
                {
                    try
                    {
                        if (!File.Exists(szPath))
                        {
                            MemoryStream stream = new MemoryStream(IncomingImage.Image);
                            DependencyService.Get<ISave>().SaveAndView($"{IncomingImage.FileName}.jpg", "image/jpeg", stream);
                        }
                        ShareFileRequest sf = new ShareFileRequest($"Sharing {szPath}", new ShareFile(szPath, "image/jpeg"));
    
                        await Share.RequestAsync(sf);
                    }
                    catch  (Exception ex)
                    { 
              Debug.WriteLine($"ImageTapHandler.cs->HandleTap()--{ex.Message}==={ex.InnerException}==={ex.StackTrace}");
            }
                    finally
                    {
                    }
                }
    

    However this code will fail to send the image every time.

        //Something went wrong, and there is no action to take
                if (IncomingImage == null || string.IsNullOrEmpty(action))
                    return;
    
                string szPath = $"{DependencyService.Get<ISave>().GetSavePath()}/{IncomingImage.FileName}.jpg";
                PhotosRepository PhotoRepo = new PhotosRepository();
    
                Debug.WriteLine(string.Format("Action: {0} On {1}", action, IncomingImage.Path));
    
                //Display share dialog
                if (action.Contains("Share"))
                {
                    try
                    {
                        if (!File.Exists(szPath))
                        {
                            MemoryStream stream = new MemoryStream(IncomingImage.Image);
                            DependencyService.Get<ISave>().SaveAndView($"{IncomingImage.FileName}.jpg", "image/jpeg", stream);
                        }
                        ShareFileRequest sf = new ShareFileRequest($"Sharing {szPath}", new ShareFile(szPath, "image/jpeg"));
    
                        await Share.RequestAsync(sf);
                    }
                    catch  (Exception ex)
                    { 
              Debug.WriteLine($"ImageTapHandler.cs->HandleTap()--{ex.Message}==={ex.InnerException}==={ex.StackTrace}");
            }
                    finally
                    {
                        if (File.Exists(szPath))
                            File.Delete(szPath);
                    }
                }
    

    The only difference between them being that I am deleting the file in the second example.
    I have tried await Share.RequestAsync(sf);, await Share.RequestAsync(sf).ConfigureAwait(false);, even tried await Share.RequestAsync(sf).ConfigureAwait(true); (even though true is the default if you do nothing), I even tried using ``await Share.RequestAsync(sf).ContinueWith(t => File.Delete(szPath);});` but in all scenarios it is considering the task as complete and awaited for when the texting app launches. I need it to do that when the app is done sending the message... I suppose an alternative would be to implement my own SMS routines and make my app double as a texting platform... (not too fond of that idea... LOL)

    So, I guess I need to know this...
    Does anyone have any good handmade garbage collector code they feel like sharing? If not I can try to write some of my own.
    I need something that will be configurable to wait x seconds (40 or so) and automatically clean a list of files I can hand off to it.
    I figure I could do it like an old messagepump like program (yay C) in essence have a loop running that has an internal dictionary that tracks a timestamp and a file name. When a file is added to the list it records the current timestamp and goes on waiting in the background, every x seconds it checks the timestamps and if they are Y seconds old it deletes the associated file.. Not entirely eloquent, but I think it may be the only way I can go about this...

    Any ideas?

    Thanks again!

  • JKnottJKnott Member ✭✭✭

    @LandLu That's what I was afraid of... Thanks for the link! I will evaluate either using that method and overloading the functionality of the Share library, or implement my garbage collector..

    Thanks for the help everyone!

Sign In or Register to comment.