【问题标题】:Async call in background task doesn't work后台任务中的异步调用不起作用
【发布时间】:2017-03-29 03:04:10
【问题描述】:

在我的应用中,我需要在后台扫描 Wi-Fi 接入点。我能够启动任务并初始化 Wi-Fi 适配器,但下一个异步调用“await scanner.ScanForNetworks()”来执行扫描似乎永远不会完成任务。会不会是大量嵌套异步调用的结果?如果是这样,有什么可能的解决方法?

ScanningTask.cs

namespace BackgroundTaskLibrary
{
    public sealed class ScanningTask : IBackgroundTask
    {
        private BackgroundTaskDeferral deferral;
        private WiFiScanner scanner = new WiFiScanner();
        private Windows.Storage.ApplicationDataContainer localSettings;
        private DispatcherTimer timer;

        public async void Run(IBackgroundTaskInstance taskInstance)
        {
            deferral = taskInstance.GetDeferral();
            taskInstance.Canceled += TaskInstance_Canceled;
            await scanner.InitializeFirstAdapter();
            ShowToastNotification("Hi", "Hello from background"); //notification is shown

            await scanner.ScanForNetworks();
            ShowToastNotification("Hi", "Hello from background1"); //this one never gets shown
            deferral.Complete();
        }

        private void TaskInstance_Canceled(IBackgroundTaskInstance sender, 
            BackgroundTaskCancellationReason reason)
        {
            deferral.Complete();
        }

        private void ShowToastNotification(string title, string stringContent)
        {
            ToastNotifier ToastNotifier = ToastNotificationManager.CreateToastNotifier();
            Windows.Data.Xml.Dom.XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
            Windows.Data.Xml.Dom.XmlNodeList toastNodeList = toastXml.GetElementsByTagName("text");
            toastNodeList.Item(0).AppendChild(toastXml.CreateTextNode(title));
            toastNodeList.Item(1).AppendChild(toastXml.CreateTextNode(stringContent));
            Windows.Data.Xml.Dom.IXmlNode toastNode = toastXml.SelectSingleNode("/toast");
            Windows.Data.Xml.Dom.XmlElement audio = toastXml.CreateElement("audio");
            audio.SetAttribute("src", "ms-winsoundevent:Notification.SMS");

            ToastNotification toast = new ToastNotification(toastXml);
            toast.ExpirationTime = DateTime.Now.AddSeconds(10);
            ToastNotifier.Show(toast);
        }
    }
}

WiFiAdapter.cs

namespace BackgroundTaskLibrary
{
    class WiFiScanner
    {
        //WiFi adapter instance
        public WiFiAdapter WiFiAdapter { get; private set; }
        public Geolocator _geolocator = new Geolocator();
        public Geoposition Location { get; set; }

        /// <summary>
        /// Find the fisrts available WiFi adapter and initialise it
        /// </summary>
        /// <returns></returns>
        public async Task InitializeFirstAdapter()
        {
            var access = await WiFiAdapter.RequestAccessAsync();
            if (access != WiFiAccessStatus.Allowed)
            {
                throw new Exception("WiFiAccessStatus not allowed");
            }
            else
            {
                var wifiAdapterResults =
                await DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
                if (wifiAdapterResults.Count >= 1)
                {
                    this.WiFiAdapter =
                    await WiFiAdapter.FromIdAsync(wifiAdapterResults[0].Id);
                }
                else
                {
                    var dialog = new MessageDialog("WiFi Adapter not found.");
                    await dialog.ShowAsync();

                    throw new Exception("WiFi Adapter not found.");
                }
            }
        }

        public async Task ScanForNetworks()
        {
            if (WiFiAdapter != null)
            {
                await WiFiAdapter.ScanAsync();
            }
            else
            {
                throw new Exception("No Wi-Fi adapter");
            }

            Location = await _geolocator.GetGeopositionAsync();

            List<AccessPoint> accessPoints = new List<AccessPoint>();
            foreach(var network in WiFiAdapter.NetworkReport.AvailableNetworks)
            {
                AccessPoint ap = new AccessPoint()
                {
                    SSID = network.Ssid,
                    Mac = network.Bssid,
                    SignalStrength = network.NetworkRssiInDecibelMilliwatts,
                    Open = (int)network.SecuritySettings.NetworkAuthenticationType
                };

                accessPoints.Add(ap);
            }
            await SaveData(accessPoints);

        }

        public async Task SaveData(List<AccessPoint> accessPoints)
        {
            string info = DateTime.Now + "|" + Location.Coordinate.Point.Position.Latitude
                    + "|" + Location.Coordinate.Point.Position.Longitude + "|";
            for (int i = 0; i < accessPoints.Count; i++)
            {
                info += accessPoints[i].ToString();
            }

            info += Environment.NewLine;

            // Get the logical root folder for all external storage devices.
            StorageFolder externalDevices = Windows.Storage.KnownFolders.RemovableDevices;

            // Get the first child folder, which represents the SD card.
            StorageFolder sdCard = (await externalDevices.GetFoldersAsync()).FirstOrDefault();


            StorageFile infoFile;

            if (sdCard != null)
            {
                //check if info file exists
                try
                {
                    infoFile = await sdCard.GetFileAsync("wifiScanInfo.txt");
                }
                catch
                {
                    infoFile = await sdCard.CreateFileAsync("wifiScanInfo.txt");
                }

                await FileIO.AppendTextAsync(infoFile, info);
            }
        }
    }
}

如果您需要整个项目:https://www.dropbox.com/sh/1v9mbr3xhgr3283/AACDbB7skZUI7Z5fiu0HT8r4a?dl=0

编辑:

通过随后简化我能够确定的 ScanningTask,问题出在调用“await WiFiAdapter.ScanAsync();”中

我终于设法从“await WiFiAdapter.ScanAsync();”中捕获了一个异常,这是堆栈跟踪:

Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))

   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()

   at BackgroundTaskLibrary.WiFiScanner.<ScanForNetworks>d__10.MoveNext()

但是扫描已完成,结果按预期保存在文本文件中。我可能缺少哪些权限?我已声明 Internet、位置和可移动存储权限。

【问题讨论】:

  • 哪个调用 excatly 没有完成?你有任何错误吗?
  • 等待扫描仪.ScanForNetworks();我没有收到任何错误
  • 你的存档有没有可能不起作用?如果您将保存降为简单,只需将“完成”写入临时文件,它是否有效?它试图将 wifiscaninfo.txt 写入哪里?
  • @BugFinder wifiscaninfo.txt 保存到我的测试设备上的 SD 卡中。此外,当整个代码不在后台任务中,而是在按钮的单击命令中时,它工作得很好。使用您的建议,我设法确定它是“等待 WiFiAdapter.ScanAsync();”这会阻止任务完成
  • 在async void 中放一个try/catch 不会有什么坏处,它甚至可以帮助你找到问题。

标签: c# asynchronous uwp background-task


【解决方案1】:

它认为异常来自在没有 requestAccessAsync 调用的情况下使用 GeoLocator :

从 Windows 10 开始,在访问用户位置之前调用 RequestAccessAsync。那时,您的应用程序必须在前台,并且必须从 UI 线程调用 RequestAccessAsync。在用户授予您的应用访问其位置的权限之前,您的应用无法访问位置数据。

来源:https://msdn.microsoft.com/en-us/library/windows/apps/windows.devices.geolocation.geolocator.aspx?f=255&MSPPError=-2147217396

【讨论】:

  • 我将 wifi 和位置的访问请求移至按钮单击命令,但没有任何改变。而且我 100% 肯定会在 await WiFiAdapter.ScanAsync(); 处引发异常,因为那是我放置 try/catch 块的地方。让我感到惊讶的是,尽管抛出了异常,但该应用程序仍在正常运行
  • 您有未处理的异常处理程序吗?因为“应用程序运行正常”确实很奇怪。
  • @HenkHolterman 我用 try/catch 包围了 'await WiFiAdapter.ScanAsync()' 并将堆栈跟踪保存到 txt 文件。最终,我最终在文本文件中同时拥有堆栈跟踪和扫描信息。
  • 我在想 Scheduler.Unhandled,也许还有 AppDomain / TaskScheduler。您的结果似乎来自 2 个并行调用。
  • 你的意思是我必须实现自定义应用级异常处理程序?
猜你喜欢
  • 2012-05-18
  • 2015-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-25
  • 1970-01-01
  • 2021-09-13
相关资源
最近更新 更多