読者です 読者をやめる 読者になる 読者になる

かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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

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