zoukankan      html  css  js  c++  java
  • 【Win 10 应用开发】在App所在的进程中执行后台任务

    在以往版本中,后台任务都是以独立的专用进程来运行,因此,定义后台任务代码的类型都要位于 Windows 运行时组件项目中。

    不过,在14393中,SDK 作了相应的扩展,不仅支持以前的独立进程中运行后台任务,也允许后台任务与应用程序位于同一个进程中执行,即单进程后台任务(Single – Process)。

    听起来很高深?其实很Easy,和以往的多进程模式的后台任务差不多,只是有以下两点不同:

    1. 对于独立进程的后台任务,实现方法是实现 IBackgroundTask 接口,然后实现 Run 方法;而如果你希望让后台任务在应用所在的进程中执行,可以重写 Application 类的 OnBackgroundActivated 方法就可以了,它类似于 IBackgroundTask 的 Run 方法。在OnBackgroundActivated方法中,你可以通过方法参数获得一个IBackgroundTaskInstance实例,所以与Run方法的处理是一样的。
    2. 在配置清单文件时,独立进程中执行的后台任务是必须指明入口点的,即后台任务类的类型名,包含命名空间路径。而如果后台任务是在应用进程中执行的话,就不需要指点入口点,因为后台任务的入口点与应用相同,就是App类。

    只要明白了以上两点,你就明白了95%了,剩下的5%,就等老周来演示给大伙瞧吧。

    App Service 的实现跟后台任务差不多,本次表演,老周就选用AppService来试水吧。

    这个示例只有小学二年级水平,它分为两个应用,一个应用具备app service,另一个应用调用它。service的功能是计算两个整数的乘积,所以说是小学二年级水平。

    先看app service的应用实现,项目模板会为我们生成一个App类,基类是Application,很简单,直接重写OnBackgroundActivated方法就行了。

     

            protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
            {
                ……
            }

     

    这个方法就相当于Run方法,所以,和以前一样,在其中添加处理代码。

                IBackgroundTaskInstance taskInstance = args.TaskInstance;
                var taskDef = taskInstance.GetDeferral();
                taskInstance.Canceled += (ca, cb) => taskDef.Complete();
    
                if (taskInstance.TriggerDetails != null && taskInstance.TriggerDetails is AppServiceTriggerDetails)
                {
                    AppServiceTriggerDetails details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
                    string appsvName = details.Name;
                    if (appsvName == "my.multip")
                    {
                        AppServiceConnection conn = details.AppServiceConnection;
                        conn.RequestReceived += async (r1, r2) =>
                        {
                            var svdef = r2.GetDeferral();
                            var request = r2.Request;
                            ValueSet inputs = request.Message;
                            int x = Convert.ToInt32(inputs["a"]);
                            int y = Convert.ToInt32(inputs["b"]);
                            int o = x * y;
                            ValueSet sendBack = new ValueSet();
                            sendBack["r"] = o;
                            await request.SendResponseAsync(sendBack);
                            svdef.Complete();
                            taskDef.Complete();
                        };
                        conn.ServiceClosed += (k1, k2) =>
                        {
                            Debug.WriteLine("app service连接关闭。");
                            taskDef.Complete();
                        };
                    }
                }

     

     

    然后就完事了,注意在清单文件中,不要指定入口点了。

          <Extensions>
            <uap:Extension Category="windows.appService">
              <uap:AppService Name="my.multip" />
            </uap:Extension>
          </Extensions>

     

     

    现在可以在另一个应用中调用了。

                AppServiceConnection conn = new AppServiceConnection();
                conn.AppServiceName = "my.multip";
                conn.PackageFamilyName = txtPkname.Text;
                var state = await conn.OpenAsync();
                if (state == AppServiceConnectionStatus.Success)
                {
                    ValueSet input = new ValueSet();
                    input["a"] = ComputeObject.Num1;
                    input["b"] = ComputeObject.Num2;
                    var response = await conn.SendMessageAsync(input);
                    if (response.Status == AppServiceResponseStatus.Success)
                    {
                        ValueSet res = response.Message;
                        ComputeObject.Result = Convert.ToInt32(res["r"]);
                    }
                }

     

    调用App Service 时,先new一个AppServiceConnection,然后指定包含app service的应用的Package的名字,这个包名可以用以下方法来获取:

    • 在包含app service的应用中,访问这个静态属性获取:Windows.ApplicationModel.Package.Current.Id.FamilyName。
    • 用VS生成项目,然后打开【输出】窗口,显示来源选择“生成”,这样你就能看到包含应用服务的应用包名了。如下图。

    00001

     

    不能使用清单文件中的包名,因为那个包名不完整。不过,你得注意了,通过【输出】窗口获取包名的时候,包的名字中要去掉版本号和平台描述,比如,我的项目中输出的生成的包名为:

    62da1ba5-7faf-4109-b82a-7a6027dbc3a3_1.0.0.0_x86__6pcpwfmxf0rfc

     

    其中,1.0.0.0是版本号,要去掉,x86是平台描述,也要干掉,后面的6pcpwfmxf0rfc可能是开发者的标识,不能去掉。最终得到需要的包名为:

    62da1ba5-7faf-4109-b82a-7a6027dbc3a3_6pcpwfmxf0rfc

    前面的GUID是应用包名字,后面要接一个下划线,然后是6pcpwfmxf0rfc。

     

    把这个最终取得的名字赋值给AppServiceConnection的PackageFamilyName属性即可,AppServiceName属性表示要调用的app service的名字。

    准备好参数后,调用OpenAsync方法打开连接,一定要先打开连接,才能调用应用服务。使用SendMessageAsync方法发送输入参数,参数是一个ValueSet对象,其实是个字典,可以自定义参数结构。在本例中,既然要计算乘法运算,当然是要传递两个整数值了。

    SendMessageAsync方法调用后,会异步返回一个AppServiceResponse实例,该实例中包含着一些从应用服务返回的内容,访问Message属性就得到应用服务响应的ValueSet,并可从中取出需要的数据,该例子中,是取出计算结果。

    好,项目干完了,咱们来试试,同时运行两个应用,然后试着调用一下应用服务。

    001

     

     

    效果已达到, 这时候,大伙可能会疑惑,如果包含app serivce的应用进程退出后,还能调用应用服务吗?没事,许多事情就是试出来的,试试看。把包含应用服务的应用进程结束掉,然后再调用一次,发现是可以成功调用的。

    有了这一招,定义后台任务就灵活很多了,既可以在独立进程中完成,也可以在应用进程中完成,具体采用哪一种,就看实际情况了。一切东西都是灵活运用的,千万不要把技术学死了。那些整天吃饱了撑着,想把什么东西都变成公式化的思想是幼稚的、死板的,这个世界上,不可以量化的事情多得很。

    不过,老周可以发表一些低见,仅作参考。如果后台任务的触发源与应用程序关系不大,比如用户登录/注销时执行的,每隔一段时间执行的(定时),这些情况,建议把后台任务写到独立的Windows运行时组件项目中,让它以独立的进程进行。

    要是后台任务是应用程序主动触发的,比如后台转码(音/视频处理),或者由应用程序使用Application Trigger触发的后台任务,都可以考虑把它归入应用程序进程中,即本文所讲述的情况。

     

    好了,今天的牛逼吹完了,该去喝点茶了(白开水最好喝,集天地灵气,无杂质,无负作用),下一篇文章咱们聊聊预启动的事情。

    示例源代码下载

     

  • 相关阅读:
    java 的 CopyOnWriteArrayList类
    AtomicInteger类的简单应用
    关于java的关键字 transient
    关于ArrayList的一些源码分析
    Eclipse新建java类的时候,自动创建注释
    关于spring-data-jpa的排序问题
    hibernate CascadeType属性
    springBoot框架不同环境读取不同的配置文件
    你迷茫的原因在于读书太少而想的太多!!!
    学历是铜牌,能力是银牌,人脉是金牌,思维是王牌
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/5978028.html
Copyright © 2011-2022 走看看