我有BackgroundTask
生成一些文字,然后将其作为文件保存到LocalFolder
。在BackgroundTask
生成后,我需要使用我的主项目(相同的VS解决方案)获取此文件,并进行下一步的工作。后台任务由用户手动触发(通过"重新加载"按钮),每隔15分钟由TimeTrigger
触发。
这是相关的代码段:
syncTrigger.RequestAsync();
articles = await getCachedArticles("");
如何告诉getCachedArticles
方法在上一次请求完成之后才能运行?非常感谢你!
答案 0 :(得分:4)
如果我理解正确,你需要做的就是等待the BackgroundTaskRegistration.Completed
event解雇。
一种方法是创建一个返回Task
的扩展方法,该方法在事件触发时完成:
public static Task<BackgroundTaskCompletedEventArgs> CompletedAsync(
this BackgroundTaskRegistration registration)
{
var tcs = new TaskCompletionSource<BackgroundTaskCompletedEventArgs>();
BackgroundTaskCompletedEventHandler handler = (s, e) =>
{
tcs.SetResult(e);
registration.Completed -= handler;
};
registration.Completed += handler;
return tcs.Task;
}
然后你会像这样使用它:
var taskCompleted = registration.CompletedAsync();
await syncTrigger.RequestAsync();
await taskCompleted;
articles = await getCachedArticles("");
请注意,代码在调用CompletedAsync()
之前调用RequestAsync()
以确保在触发任务之前注册偶数处理程序,以避免任务完成时的竞争条件在处理程序注册之前。
答案 1 :(得分:0)
每当您希望当前上下文在继续之前等待异步方法完成时,您希望使用await
关键字:
await syncTrigger.RequestAsync(); //the line below will not be executed until syncTrigger.RequestAsync() completes its task
articles = await getCachedArticles("");
我建议您阅读await C# Reference Article,以全面了解其工作原理。
答案 2 :(得分:0)
编辑:svick's answer显示了最好的方法,我甚至写了一篇关于它的blog post。以下是我的原始答案,其中包含一些可能适用于某些情况的间接替代方案。
正如其他人所指出的那样,等待syncTrigger.RequestAsync()
不会有帮助,尽管不过这是一个好主意。当后台任务被成功触发时,它将恢复执行,因此允许您检查它是否因任何原因失败。
当您从应用程序触发后台任务时,您可以创建app service以使其正常工作。应用服务的行为与Web服务类似。它们在后台任务中运行,但具有请求 - 响应语义。
在后台服务中,您需要处理RequestReceived
事件:
public void Run(IBackgroundTaskInstance taskInstance)
{
var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
appServiceconnection = details.AppServiceConnection;
appServiceconnection.RequestReceived += OnRequestReceived;
}
private async void OnRequestReceived(AppServiceConnection sender,
AppServiceRequestReceivedEventArgs args)
{
var messageDeferral = args.GetDeferral();
ValueSet arguments = args.Request.Message;
ValueSet result = new ValueSet();
// read values from arguments
// do the processing
// put data for the caller in result
await args.Request.SendResponseAsync(result);
messageDeferral.Complete();
}
在客户端中,您可以调用应用服务:
var inventoryService = new AppServiceConnection();
inventoryService.AppServiceName = "from manifest";
inventoryService.PackageFamilyName = "from manifest";
var status = await inventoryService.OpenAsync();
var arguments = new ValueSet();
// set the arguments
var response = await inventoryService.SendMessageAsync(arguments);
if (response.Status == AppServiceResponseStatus.Success)
{
var result = response.Message;
// read data from the result
}
检查linked page以获取有关应用服务的更多信息。
您也可以从预定的后台任务中调用相同的应用服务,但在这种情况下处理完成时无法获得通知。
由于您已经提到您通过LocalFolder
中的文件交换数据,您的应用可以尝试监控对该文件的更改:
private async Task Init()
{
var storageFolder = ApplicationData.Current.LocalFolder;
var monitor = storageFolder.CreateFileQuery();
monitor.ContentsChanged += MonitorContentsChanged;
var files = await monitor.GetFilesAsync();
}
private void MonitorContentsChanged(IStorageQueryResultBase sender, object args)
{
// react to the file change - should mean the background task completed
}
据我所知,您只能监视文件夹中的所有更改,并且无法确定事件处理程序内部的更改,因此对于您的情况,最好只包含一个单独的子文件夹后台任务完成后保存的文件。这样,只有在需要时才会引发事件。
但是,您必须亲自检查一下这种方法是否足够可靠。
答案 3 :(得分:-3)
您需要等待使用结果完成的RequestAsync方法 https://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.background.devicetriggerresult
之后我建议用task.delay等几秒钟,然后尝试获取数据。
更新: 当您拥有设备触发结果时,您需要先检查此结果,然后尝试获取数据,之后您可以假设已保存数据。我之前建议使用Task.Delay只是等待几秒钟以确保所有数据都被保存,因为有时这个过程比预期的要多几毫秒。我这样做是因为我们没有像TriggerCompleted这样的事件我需要创建自己的方法。 我之前在我的应用程序中做过这个并且效果很好。