かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

UWPでバックグラウンドタスクとフォアグラウンドの処理の連携方法

UWPのバックグラウンドタスクとフォアグラウンドの処理を連携させるために、IBackgroundTaskRegistrationのProgressイベントとCompletedイベントが定義されています。

例えば、TimerTaskという名前のバックグラウンドタスクのProgressイベントとCompletedイベントを購読するには以下のようになります。

using System;
using System.Linq;
using Windows.ApplicationModel.Background;
using Windows.Storage;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

// 空白ページのアイテム テンプレートについては、http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 を参照してください

namespace App10
{
    /// <summary>
    /// それ自体で使用できる空白ページまたはフレーム内に移動できる空白ページ。
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            var task = BackgroundTaskRegistration.AllTasks
                .First(x => x.Value.Name == "TimerTask")
                .Value;
            task.Progress += this.Task_Progress;
            task.Completed += this.Task_Completed;
        }

        private async void Task_Progress(BackgroundTaskRegistration sender, BackgroundTaskProgressEventArgs args)
        {
            await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                this.TextBlock.Text = $"{args.Progress}%";
            });
        }

        private async void Task_Completed(BackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs args)
        {
            await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                this.TextBlock.Text = (string)ApplicationData.Current.LocalSettings.Values["TimerTask"];
            });
        }
    }
}

画面にTextBlockという名前のTextBlockを置いている前提のコードになります。ProgressイベントではそのままProgressの状態を表示して、CompletedではApplicationData.Current.LocalSettingsを経由してバックグラウンドタスクからのデータを表示しています。

バックグラウンドタスク側は以下のような実装になっています。

using System;
using Windows.ApplicationModel.Background;
using Windows.Storage;
using Windows.System.Threading;

namespace RuntimeComponent1
{
    public sealed class TimerTask : IBackgroundTask
    {
        private BackgroundTaskDeferral Deferral { get; set; }
        private uint Progress { get; set; }
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            this.Deferral = taskInstance.GetDeferral();
            ThreadPoolTimer.CreatePeriodicTimer(timer =>
            {
                if (this.Progress == 100)
                {
                    this.Deferral.Complete();
                    timer.Cancel();
                    ApplicationData.Current.LocalSettings.Values["TimerTask"] = DateTime.Now.ToString();
                    return;
                }

                taskInstance.Progress = this.Progress;
                this.Progress += 10;
            }, TimeSpan.FromSeconds(1));
        }
    }
}

タイマーでカウントアップして最後にApplicationData.Current.LocalSettingsにデータを書き込んで終了しています。

バックグラウンドタスクの登録はApp.xaml.csのOnLaunchedメソッドで以下のコードを実行しています。

foreach (var t in BackgroundTaskRegistration.AllTasks)
{
    t.Value.Unregister(true);
}
var task = BackgroundTaskRegistration.AllTasks
    .FirstOrDefault(x => x.Value.Name == "TimerTask")
    .Value;
if (task == null)
{
    var tb = new BackgroundTaskBuilder();
    tb.Name = "TimerTask";
    tb.TaskEntryPoint = "RuntimeComponent1.TimerTask";
    tb.SetTrigger(new TimeTrigger(15, false));
    task = tb.Register();
}

Package.appxmanifestへタイマーとしてバックグラウンドタスクの宣言を追加するのを忘れずに。

Tipsバックグラウンドタスクのデバッグ

デバッグの場所のライフサイクルイベントから、バックグラウンドタスクのキックが出来ます。

f:id:okazuki:20160407111006p:plain