1

I am working on a project where I play a couple of videos. To be able to play those videos I need to fetch their data when requested by the app through HttpClient from System.Net.Http. Everything works normally and the UWP app downloads required information from Internet when the app is in foreground.

As soon as the app leaves foreground and gets minimized by user the HttpClient, whether it's from System.Net.Http namesoace or Windows.Web.Http namespace, does not work. I have even tried to set a breakpoint there and as soon as I move forward HttpClient does not respond to the await client.GetAsync() method and stays there without returning any result unless you activate the app again and then it returns any value. I cant use BackgroundTasks because they need Triggers but I need to access data on demand.

I have also read this article describing to request ExtendedExecutionState but the result is still the same. I request this state when the app gets a request to play a list of items. Even after getting the result as Allowed, the HttpClient has the same behavior as above.

Is there any method that can execute internet related queries when requested and get some information?

The code that I execute to access data from internet is:

public static async Task<string> GetResponseDataFromAPI(string apiRequestUrl, CancellationTokenSource cts = default)
{
    cts = cts ?? new CancellationTokenSource(TimeSpan.FromSeconds(20));
    try
    {
        var responseData = await AuthRequestHelper.globalHttpClient.GetAsync(apiRequestUrl, cts.Token);
        var result = await responseData.Content.ReadAsStringAsync();
        return result;
    }
    catch (Exception ex)
    {
        INotifyHelper.iNotifyObject.IsVideoLoading = false;
        INotifyHelper.iNotifyObject.IsDataLoading = false;
        // show error
        return "{ \"error\": {\"code\": " + $"\"{ex.HResult}\", \"message\": \"{ex.Message}\"" + "} }";
    }
}

The returned value is then analyzed to get required data from it.

The code that is executed to request ExtendedExecutionSession is:

public static async void RequestBackgroundExtendedExecution()
{
    using (var session = new ExtendedExecutionSession())
    {
        session.Reason = ExtendedExecutionReason.Unspecified;
        session.Description = "Background Playlist Playback";
        session.Revoked += Session_Revoked;
        if (await session.RequestExtensionAsync() is ExtendedExecutionResult result)
        {
            if (result == ExtendedExecutionResult.Denied)
            {
                // show user that background playlist playback will not be possible
            }
        }
    }
}

Is there anything that can be done to execute the requested operations while in minimized state? A little help to point in the right direction will be much appreciated. Thanks

Lauren Rutledge
  • 1,195
  • 5
  • 18
  • 27
Ahmar
  • 740
  • 6
  • 26
  • 2
    There was a topic about this a few weeks ago https://stackoverflow.com/questions/47339719/how-to-disable-uwp-app-suspension it turns you can tell the linker that youw uwp app is only intended for PC/Windows. In that mode, it is possible to circumvent it see https://blogs.msdn.microsoft.com/mvpawardprogram/2018/01/30/non-suspending-uwp-desktop-apps/#comment-130715 – Goodies Aug 08 '18 at 18:04

2 Answers2

1

The BackgroundTransfer classes are designed specifically to download data in the background even if the app is suspended or closed.

HttpClient is better for very short downloads that will complete quickly. For lengthy downloads such as videos use Windows.Networking.BackgroundTransfer.BackgroundDownloader. If you need to process the file when the download completes you can use that completion as a trigger for a background task.

See the provided documentation links for overviews and how tos. There is a full sample at https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/BackgroundTransfer

Rob Caplan - MSFT
  • 21,714
  • 3
  • 32
  • 54
  • I think you did not quite understand my question, I use `HttpClient` to get the ID of the video or the URL. I do not download the entire video with `HttpClient`. Is `BackgroundTransfer` still applicable for such short fetching operations? – Ahmar Aug 09 '18 at 06:53
  • As a matter of fact, no `await` function runs if the app is minimized. It waits for the app to come to foreground and then continues, otherwise it's kind of paused. So I decided to use the `Task.Result` method since the user is not using the app I can get the results for async methods without blocking the app UI. – Ahmar Aug 10 '18 at 03:16
0

So I decided to write my own answer here because I am not really sure if someone else might get to this issue. One thing that I suspect is that I recently updated C# Language to v7.3 because VS 2017 said so while writing some code. Maybe the issue is related to that but so far my solution works in the minimized state.

I also want to state here that I implemented BackgroundTask first to overcome this problem using ApplicationTrigger to trigger the process when needed but it was kind of a hack honestly. Have a look at the code:

public static async Task<string> GetBackgroundTaskReturnValue(string apiRequestUrl)
{
        StaticItemsHelper.IsBackgroundPlaylistTaskRunning = true;
        if (StaticItemsHelper.IsBackgroundPlaylistTaskRunning)
        {
            for (int seconds = 0; seconds < 20;)
            {
                if (!StaticItemsHelper.IsBackgroundPlaylistTaskRunning)
                {
                    break;
                }
                else
                {
                    Task.Delay(1000).Wait();
                }
            }
        }
        var request = BackgroundTaskHelper.BackgroundPlaylistTrigger.RequestAsync().GetResults();
        if (request == Windows.ApplicationModel.Background.ApplicationTriggerResult.Allowed)
        {
            SettingsHelper.localSettings.Values[SettingsHelper.BackgroundPlaylistPlaybackURLKey] = apiRequestUrl;
            SettingsHelper.localSettings.Values[SettingsHelper.BackgroundPlaylistPlaybackTokenKey] = StaticItemsHelper.CurrentUserAccessToken;
            if (SettingsHelper.tempFolder.TryGetItemAsync(SettingsHelper.BackgroundPlaylistPlaybackReturnKey).GetResults() is StorageFile file)
            {
                await file.DeleteAsync();
            }
            for (int seconds = 0; seconds < 30;)
            {
                if (SettingsHelper.tempFolder.TryGetItemAsync(SettingsHelper.BackgroundPlaylistPlaybackReturnKey).GetResults() is StorageFile _rfile)
                {
                    var _returnVal = FileIO.ReadTextAsync(_rfile).GetResults();
                    if (!string.IsNullOrEmpty(_returnVal.ToString()))
                    {
                        await _rfile.DeleteAsync();
                        SettingsHelper.localSettings.Values.Remove(SettingsHelper.BackgroundPlaylistPlaybackTokenKey);
                        SettingsHelper.localSettings.Values.Remove(SettingsHelper.BackgroundPlaylistPlaybackURLKey);
                        StaticItemsHelper.IsBackgroundPlaylistTaskRunning = false;
                        return _returnVal;
                    }
                }
                Task.Delay(2000).Wait();
                seconds += 2;
            }
        }
        else if (request == Windows.ApplicationModel.Background.ApplicationTriggerResult.CurrentlyRunning)
        {
            for (int seconds = 0; seconds < 30;)
            {
                Task.Delay(2000).Wait();
                seconds += 2;
                request = BackgroundTaskHelper.BackgroundPlaylistTrigger.RequestAsync().GetResults();
                if (request == Windows.ApplicationModel.Background.ApplicationTriggerResult.Allowed)
                {
                    return GetBackgroundTaskReturnValue(apiRequestUrl).Result;
                }
            }
        }
        if (SettingsHelper.tempFolder.TryGetItemAsync(SettingsHelper.BackgroundPlaylistPlaybackReturnKey).GetResults() is StorageFile _file)
        {
            await _file.DeleteAsync();
        }
        SettingsHelper.localSettings.Values.Remove(SettingsHelper.BackgroundPlaylistPlaybackTokenKey);
        SettingsHelper.localSettings.Values.Remove(SettingsHelper.BackgroundPlaylistPlaybackURLKey);
        StaticItemsHelper.IsBackgroundPlaylistTaskRunning = false;
        return "{ \"error\": {\"code\": \"NetworkError\", \"message\": \"Server returned nothing.\"} }";
}

Actual Solution: But then it was also not working as I expected so I decided to drop it and used the following code instead. (Compare this with the question)

public static async Task<string> GetResponseDataFromAPI(string apiRequestUrl, CancellationTokenSource cts = default)
{
        cts = cts ?? new CancellationTokenSource(TimeSpan.FromSeconds(20));
        try
        {
            if (StaticItemsHelper.IsAppInBackground)
            {
                //return await ViewHelper.GetBackgroundTaskReturnValue(apiRequestUrl);
                var responseData = AuthRequestHelper.globalHttpClient.GetAsync(apiRequestUrl, cts.Token).Result;
                var result = responseData.Content.ReadAsStringAsync().Result;
                return result;
            }
            else
            {
                var responseData = await AuthRequestHelper.globalHttpClient.GetAsync(apiRequestUrl, cts.Token);
                var result = await responseData.Content.ReadAsStringAsync();
                return result;
            }
        }
        catch (Exception ex)
        {
            INotifyHelper.iNotifyObject.IsDataLoading = false;
            // show error
            return "{ \"error\": {\"code\": " + $"\"{ex.HResult}\", \"message\": \"{ex.Message}\"" + "} }";
        }
}

Hopefully, this helps someone else.

Ahmar
  • 740
  • 6
  • 26