zoukankan      html  css  js  c++  java
  • 背水一战 Windows 10 (114)

    [源码下载]


    背水一战 Windows 10 (114) - 后台任务: 后台任务的 Demo(与 app 不同进程), 后台任务的 Demo(与 app 相同进程)



    作者:webabcd


    介绍
    背水一战 Windows 10 之 后台任务

    • 后台任务的 Demo(与 app 不同进程)
    • 后台任务的 Demo(与 app 相同进程)



    示例
    1、演示后台任务的应用(后台任务与 app 不同进程)
    /BackgroundTaskLib/BackgroundTaskDemo.cs

    /*
     * 后台任务
     * 
     * 注:
     * 后台任务项目的输出类型需要设置为“Windows 运行时组件”,其会生成 .winmd 文件,winmd - Windows Metadata
     * 
     * 另:
     * 在用户设置的免打扰时间内,所有后台任务均暂停(来电和闹钟例外),免打扰时间过后,后台任务将随机在不同时间点启动
     */
    
    using System;
    using System.Threading.Tasks;
    using Windows.ApplicationModel.Background;
    using Windows.Storage;
    
    namespace BackgroundTaskLib
    {
        // 实现 IBackgroundTask 接口,其只有一个方法,即 Run()
        public sealed class BackgroundTaskDemo : IBackgroundTask
        {
            public async void Run(IBackgroundTaskInstance taskInstance)
            {
                // 后台任务在执行中被终止执行时所触发的事件
                taskInstance.Canceled += taskInstance_Canceled;
    
                // 异步操作
                BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
    
                try
                {
                    // 指定后台任务的进度
                    taskInstance.Progress = 0;
                    // taskInstance.InstanceId - 后台任务实例的唯一标识,由系统生成,与前台的 IBackgroundTaskRegistration.TaskId 一致
                    // taskInstance.SuspendedCount - 由资源管理政策导致后台任务挂起的次数
    
                    StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(@"webabcdBackgroundTaskdemo.txt", CreationCollisionOption.ReplaceExisting);
                    for (uint progress = 10; progress <= 100; progress += 10)
                    {
                        await Task.Delay(1000);
    
                        // 更新后台任务的进度(会通知给前台)
                        taskInstance.Progress = progress;
    
                        // 获取当前后台任务的开销量(Low, Medium, High)
                        // BackgroundWorkCostValue bwcv = Windows.ApplicationModel.Background.BackgroundWorkCost.CurrentBackgroundWorkCost;
    
                        // 写入相关数据到指定的文件
                        await FileIO.AppendTextAsync(file, "progress: " + progress.ToString() + ", currentTime: " + DateTime.Now.ToString() + Environment.NewLine);
                    }
                }
                finally
                {
                    // 完成异步操作
                    deferral.Complete();
                }
            }
    
            void taskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
            {
                // 如果这里 5 秒内没有完成,则系统会终止该应用,并生成错误报告上传至 windows 商店的开发人员账户
    
                /*
                 * BackgroundTaskCancellationReason - 后台任务在执行中被终止执行的原因
                 *     Abort - 前台 app 调用了 IBackgroundTaskRegistration.Unregister(true)
                 *     Terminating - 因为系统策略,而被终止
                 *     LoggingOff - 因为用户注销系统而被取消
                 *     ServicingUpdate - 因为 app 更新而被取消
                 *     ... - 还有好多,参见文档吧
                 */
            }
        }
    }

    BackgroundTask/Demo.xaml

    <Page
        x:Class="Windows10.BackgroundTask.Demo"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.BackgroundTask"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <TextBlock Name="lblMsg" Margin="5" />
    
                <Button Name="btnRegister" Content="注册一个后台任务" Margin="5" Click="btnRegister_Click" />
                <Button Name="btnUnregister" Content="注销指定的后台任务" Margin="5" Click="btnUnregister_Click" />
    
            </StackPanel>
        </Grid>
    </Page>

    BackgroundTask/Demo.xaml.cs

    /*
     * 演示后台任务的应用(后台任务与 app 不同进程)
     * 
     * 注:
     * 1、需要引用后台任务项目,相关代码参见 BackgroundTaskLib/BackgroundTaskDemo.cs
     * 2、需要在 Package.appxmanifest 添加“后台任务”声明,支持的任务类型选择“系统事件”,并指定 EntryPoint(后台任务的类全名),类似如下:
     * <Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTaskLib.BackgroundTaskDemo">
     *   <BackgroundTasks>
     *     <Task Type="systemEvent" />
     *   </BackgroundTasks>
     * </Extension>
     */
    
    using System;
    using System.Collections.Generic;
    using Windows.ApplicationModel;
    using Windows.ApplicationModel.Background;
    using Windows.Storage;
    using Windows.UI.Core;
    using Windows.UI.Popups;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Navigation;
    
    namespace Windows10.BackgroundTask
    {
        public sealed partial class Demo : Page
        {
            // 所注册的后台任务的名称
            private string _taskName = "Demo";
    
            // 所注册的后台任务的 EntryPoint,即后台任务的类全名
            // 对应 Package.appxmanifest 中的“后台任务”声明,类似如下:<Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTaskLib.BackgroundTaskDemo" />
            private string _taskEntryPoint = "BackgroundTaskLib.BackgroundTaskDemo";
    
            // 后台任务是否已在系统中注册
            private bool _taskRegistered = false;
    
            // 后台任务执行状况的进度说明
            private string _taskProgress = "";
    
            public Demo()
            {
                this.InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                // 遍历所有已注册的后台任务(避免重复注册)
                foreach (KeyValuePair<Guid, IBackgroundTaskRegistration> task in BackgroundTaskRegistration.AllTasks)
                {
                    if (task.Value.Name == _taskName)
                    {
                        // 如果找到了指定的后台任务,则为其增加 Progress 和 Completed 事件监听,以便前台 app 接收后台任务的进度汇报和完成汇报
                        AttachProgressAndCompletedHandlers(task.Value);
                        _taskRegistered = true;
                        break;
                    }
                }
    
                UpdateUI();
            }
    
            private async void btnRegister_Click(object sender, RoutedEventArgs e)
            {
                // 在注册后台任务之前,需要调用 BackgroundExecutionManager.RequestAccessAsync(),如果是更新过的 app 则在之前还需要调用 BackgroundExecutionManager.RemoveAccess()
                string appVersion = $"{Package.Current.Id.Version.Major}.{Package.Current.Id.Version.Minor}.{Package.Current.Id.Version.Build}.{Package.Current.Id.Version.Revision}";
                if ((string)ApplicationData.Current.LocalSettings.Values["AppVersion"] != appVersion)
                {
                    // 对于更新的 app 来说先要调用这个方法
                    BackgroundExecutionManager.RemoveAccess();
                    // 注册后台任务之前先要调用这个方法,并获取 BackgroundAccessStatus 状态
                    BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
                    if (status == BackgroundAccessStatus.Unspecified || status == BackgroundAccessStatus.DeniedBySystemPolicy || status == BackgroundAccessStatus.DeniedByUser)
                    {
                        // 无权限注册后台任务
    
                        await new MessageDialog("没有权限注册后台任务").ShowAsync();
                    }
                    else
                    {
                        // 有权限注册后台任务
    
                        ApplicationData.Current.LocalSettings.Values["AppVersion"] = appVersion;
                    }
                }
    
    
                // 用于构造一个后台任务
                BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
    
                builder.Name = _taskName; // 后台任务的名称
                builder.TaskEntryPoint = _taskEntryPoint; // 后台任务入口点,即后台任务的类全名
    
                /*
                 * 后台任务触发器 IBackgroundTrigger
                 *     TimeTrigger - 循环触发器,需要 app 在锁屏上,最小周期 15 分钟
                 *     MaintenanceTrigger - 循环触发器,与 TimeTrigger 类似,但是不要求 app 在锁屏上,最小周期 15 分钟
                 *     SystemTrigger - 一般不要求 app 在锁屏上(实例化时的第二个参数是 oneShot,其代表是否只触发一次)
                 *         SmsReceived - 接收到新的 sms 消息时
                 *         LockScreenApplicationAdded - app 添加到锁屏时
                 *         LockScreenApplicationRemoved - app 从锁屏移除时
                 *         OnlineIdConnectedStateChange - 当前连接的 Microsoft 帐户更改时
                 *         TimeZoneChange - 时区发生更改时
                 *         ServicingComplete - 系统完成了 app 的更新时
                 *         ControlChannelReset - 重置 ControlChannel 时,需要 app 在锁屏上
                 *         NetworkStateChange - 网络状态发生改变时
                 *         InternetAvailable - Internet 变为可用时
                 *         SessionConnected - 会话状态连接时,需要 app 在锁屏上
                 *             这里的 Session 指的是,用户与本机之间的 Session,也就是说当切换用户时 Session 会发生改变
                 *         UserPresent - 用户变为活动状态时,需要 app 在锁屏上
                 *         UserAway - 用户变为非活动状态时,需要 app 在锁屏上
                 *         PowerStateChange - 当 Windows.System.Power.PowerManager.BatteryStatus 发生变化时
                 *     ToastNotificationHistoryChangedTrigger - 当 toast 通知从 ToastNotificationHistory 中添加或删除时
                 *     PushNotificationTrigger - 需要 app 在锁屏上。关于“推送通知”请参见:BackgroundTask/PushNotification.xaml
                 *     ControlChannelTrigger - 需要 app 在锁屏上。关于“推送通道”请参见:BackgroundTask/ControlChannel.xaml
                 */
                builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false));
    
                /*
                 * 后台任务执行条件 SystemConditionType,当后台任务触发器触发后,只有满足了指定的条件才能执行(可以添加多个条件,也可以一个条件都不添加)
                 *     UserPresent - 用户为活动状态
                 *     UserNotPresent - 用户为非活动状态
                 *     InternetAvailable - Internet 状态为可用
                 *     InternetNotAvailable - Internet 状态为不可用
                 *     SessionConnected - 会话状态是连接的。这里的 Session 指的是,用户与本机之间的 Session,也就是说如果系统中有用户登录则 SessionConnected
                 *     SessionDisconnected - 会话状态是断开的。这里的 Session 指的是,用户与本机之间的 Session,也就是说如果系统中没有用户登录(所有用户都注销了)则 SessionDisconnected
                 *     FreeNetworkAvailable - 免费网络(比如 wifi)
                 *     BackgroundWorkCostNotHigh - 后台任务开销较低
                 */
                // builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
    
                // 在后台任务运行过程中,如果发现后台任务执行条件至少有一条不符合要求时,是否取消此后台任务的执行(默认值为 false)
                // builder.CancelOnConditionLoss = false;
    
                // 设置后台任务的所属组,以便组操作(关于 BackgroundTaskRegistrationGroup 和 BackgroundTaskRegistration 的组相关的操作请参见文档)
                // builder.TaskGroup = new BackgroundTaskRegistrationGroup("myGroup");
    
    
                /*
                 * 再说明一下
                 * SetTrigger() 用于指定什么时候触发后台任务
                 * AddCondition() 用于指定在后台任务被触发时,其必须满足什么条件才能被执行
                 * CancelOnConditionLoss 用于指定在后台任务的执行过程中,如果发现 AddCondition() 中的条件不满足了,是否要取消此后台任务的执行
                 */
    
    
                // 向系统注册此后台任务
                BackgroundTaskRegistration task = builder.Register();
                // task.TaskId; 获取此后台任务的标识,一个 GUID(其与后台任务类中的 taskInstance.InstanceId 一致)
    
                // 为此后台任务增加 Progress 和 Completed 事件监听,以便前台 app 接收后台任务的进度汇报和完成汇报
                AttachProgressAndCompletedHandlers(task);
    
                _taskRegistered = true;
    
                UpdateUI();
            }
    
            private void btnUnregister_Click(object sender, RoutedEventArgs e)
            {
                // 遍历所有已注册的后台任务
                foreach (KeyValuePair<Guid, IBackgroundTaskRegistration> task in BackgroundTaskRegistration.AllTasks)
                {
                    if (task.Value.Name == _taskName)
                    {
                        // 从系统中注销指定的后台任务。唯一一个参数代表如果当前后台任务正在运行中,是否需要将其取消
                        task.Value.Unregister(true);
                        break;
                    }
                }
    
                _taskRegistered = false;
    
                UpdateUI();
            }
    
            private void AttachProgressAndCompletedHandlers(IBackgroundTaskRegistration task)
            {
                // 为任务增加 Progress 和 Completed 事件监听,以便前台 app 接收后台任务的进度汇报和完成汇报
                task.Progress += new BackgroundTaskProgressEventHandler(OnProgress);
                task.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);
            }
    
            private void OnProgress(IBackgroundTaskRegistration task, BackgroundTaskProgressEventArgs args)
            {
                // 获取后台任务的执行进度
                _taskProgress = args.Progress.ToString();
    
                UpdateUI();
            }
    
            private void OnCompleted(IBackgroundTaskRegistration task, BackgroundTaskCompletedEventArgs args)
            {
                // 后台任务已经执行完成
                _taskProgress = "完成";
    
                // 如果此次后台任务的执行出现了错误,则调用 CheckResult() 后会抛出异常
                try
                {
                    args.CheckResult();
                }
                catch (Exception ex)
                {
                    _taskProgress = ex.ToString();
                }
    
                UpdateUI();
            }
    
            private async void UpdateUI()
            {
                await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    btnRegister.IsEnabled = !_taskRegistered;
                    btnUnregister.IsEnabled = _taskRegistered;
    
                    if (_taskProgress != "")
                        lblMsg.Text = "进度:" + _taskProgress;
                });
            }
        }
    }


    2、演示后台任务的应用(后台任务与 app 相同进程)
    App.xaml.cs

            // 后台任务与 app 相同进程时,后台任务的实现逻辑
            // 这部分与“后台任务与 app 不同进程”中的后台任务的编写基本一致,详细可参见 /BackgroundTaskLib/BackgroundTaskDemo.cs
            // 后台任务与前台的通信也可以参见“后台任务与 app 不同进程”示例,不过由于本后台任务示例是和 app 同进程的,所以通过内存通信会更简单
            protected async override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
            {
                // 获取 IBackgroundTaskInstance 对象
                IBackgroundTaskInstance taskInstance = args.TaskInstance;
    
                // 异步操作
                BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
    
                try
                {
                    // 写入相关数据到文件
                    StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(@"webabcdBackgroundTaskdemoInProcess.txt", CreationCollisionOption.ReplaceExisting);
                    await FileIO.AppendTextAsync(file, "background task in process: " + DateTime.Now.ToString() + Environment.NewLine);
    
                }
                finally
                {
                    // 完成异步操作
                    deferral.Complete();
                }
            }

    BackgroundTask/DemoInProcess.xaml

    <Page
        x:Class="Windows10.BackgroundTask.DemoInProcess"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.BackgroundTask"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <Button Name="btnRegister" Content="注册一个后台任务" Margin="5" Click="btnRegister_Click" />
    
            </StackPanel>
        </Grid>
    </Page>

    BackgroundTask/DemoInProcess.xaml.cs

    /*
     * 演示后台任务的应用(后台任务与 app 相同进程)
     * 
     * 注:
     * 1、后台任务与 app 不同进程,详见 /BackgroundTask/Demo.xaml.cs
     * 2、此示例不需要在 Package.appxmanifest 添加“后台任务”声明
     * 3、此示例不需要引用后台任务项目,而是在 App.xaml.cs 中通过 override void OnBackgroundActivated(BackgroundActivatedEventArgs args) 来编写后台任务的逻辑
     */
    
    using System;
    using System.Collections.Generic;
    using Windows.ApplicationModel;
    using Windows.ApplicationModel.Background;
    using Windows.Storage;
    using Windows.UI.Popups;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    namespace Windows10.BackgroundTask
    {
        public sealed partial class DemoInProcess : Page
        {
            // 所注册的后台任务的名称
            private string _taskName = "DemoInProcess";
    
            public DemoInProcess()
            {
                this.InitializeComponent();
            }
    
            private async void btnRegister_Click(object sender, RoutedEventArgs e)
            {
                // 在注册后台任务之前,需要调用 BackgroundExecutionManager.RequestAccessAsync(),如果是更新过的 app 则在之前还需要调用 BackgroundExecutionManager.RemoveAccess()
                string appVersion = $"{Package.Current.Id.Version.Major}.{Package.Current.Id.Version.Minor}.{Package.Current.Id.Version.Build}.{Package.Current.Id.Version.Revision}";
                if ((string)ApplicationData.Current.LocalSettings.Values["AppVersion"] != appVersion)
                {
                    // 对于更新的 app 来说先要调用这个方法
                    BackgroundExecutionManager.RemoveAccess();
                    // 注册后台任务之前先要调用这个方法,并获取 BackgroundAccessStatus 状态
                    BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
                    if (status == BackgroundAccessStatus.Unspecified || status == BackgroundAccessStatus.DeniedBySystemPolicy || status == BackgroundAccessStatus.DeniedByUser)
                    {
                        // 无权限注册后台任务
    
                        await new MessageDialog("没有权限注册后台任务").ShowAsync();
                    }
                    else
                    {
                        // 有权限注册后台任务
    
                        ApplicationData.Current.LocalSettings.Values["AppVersion"] = appVersion;
                    }
                }
    
    
                // 如果任务已注册,则注销
                foreach (KeyValuePair<Guid, IBackgroundTaskRegistration> t in BackgroundTaskRegistration.AllTasks)
                {
                    if (t.Value.Name == _taskName)
                    {
                        t.Value.Unregister(true);
                    }
                }
    
                // 注册后台任务,后台任务的代码参见 App.xaml.cs 中的 OnBackgroundActivated(BackgroundActivatedEventArgs args) 方法
                BackgroundTaskBuilder builder = new BackgroundTaskBuilder
                {
                    Name = _taskName,
                };
                builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false));
                BackgroundTaskRegistration task = builder.Register();
            }
        }
    }



    OK
    [源码下载]

  • 相关阅读:
    Effective Java 第三版——26. 不要使用原始类型
    Effective Java 第三版——25. 将源文件限制为单个顶级类
    Effective Java 第三版——24. 优先考虑静态成员类
    Effective Java 第三版——23. 优先使用类层次而不是标签类
    Effective Java 第三版——22. 接口仅用来定义类型
    Effective Java 第三版——21. 为后代设计接口
    Effective Java 第三版——20. 接口优于抽象类
    Effective Java 第三版——19. 如果使用继承则设计,并文档说明,否则不该使用
    Effective Java 第三版——18. 组合优于继承
    Effective Java 第三版——17. 最小化可变性
  • 原文地址:https://www.cnblogs.com/webabcd/p/9211713.html
Copyright © 2011-2022 走看看