Xamarin Getting Started翻译系列七--创建服务

本文讲述Android服务,是Android运行在后台的组件。并解释服务可用于不同的场景,以及如何实现长时间运行后台任务、为远程调用提供接口。

This article covers Android services, which are Androidcomponents that allow work to be done in the background. It explains thedifferent scenarios that services are suited for and shows how to implementthem both for performing long-running background tasks as well as for providingan interface for remote procedure calls.

概述

使用服务可使业务在后台执行。Activity一样,他们具有自己的生命周期,调用者关闭后还可以继续执行。另外可与调用者在同一个进程,也可在独立进程中。

Servicesenable tasks to be performed in the background. They run in their ownlifecycles, so they can continue to operate even after the caller, such as anActivity, exits. Additionally, they can run locally, in the same process as thecaller, as well as in separate processes.

有两种方式使用服务:

可用于与调用者无交互的执行长时间运行的任务。例如在后台下载信息的服务。这种服务叫做启动服务(Started Services)。

提供程序接口与调用者交互,例如系统包含的定位服务,提供各种方法给应用程序获取当前位置信息。这种服务叫做绑定服务( Bound Services

他们不是独立存在的。有的服务即是启动服务(Started Services)也是绑定服务(Bound Services),一直运行到被显式的停止而且没有客户端与之绑定,或者系统由于内存紧缺将他关闭。

Thereare two fundamental ways that services can be used:

·        They can be used to perform somelong running task without any need for interaction with a caller other than therequest that some task be performed. An example of this would be a service thatdownloads some information in the background. These services are referred toas Started Services.

·        They can provide a programmaticinterface for callers to interact with, such as a location service, which thesystem already includes, that provides a variety of methods that applicationscan use to glean information about the current location. These services arereferred to as Bound Services.

Thesearen’t mutually exclusive. The same service can be both a Started Service and aBound Service and will run until it is explicitly stopped and no clients arebound to it, or the system shuts it down under memory pressure.

本文讨论这如何开发两种类型服务。对于自启动服务(Started Services)讲述:

服务生命周期

线程

控制由于系统内存不足而关闭的服务重启

使用IntentService类简化服务开发

通知用户

对于绑定服务(Bound Services)讲述:

创建绑定服务(Bound Service

与自启动服务(Started Service)生命周期的差别

创建一个进程内,同时也是跨进程的服务

本文同时演示如果显示设备上运行服务的信息。最后,用股票的例子作为结尾来阐述上面的概念,以及服务开发中有用的模式。

Thisarticle discusses how to develop services of either type. For Started Servicesit explains:

·        The service lifecycle.

·        Threading.

·        Controlling service restarts dueto service shutdown by the system under memory pressure.

·        Simplifying service developmentusing the IntentService class.

·        Notifying users.

ForBound Services it examines:

·        Creating a bound service.

·        How the lifecycle differs fromStarted Services.

·        Creating in-process, as well asinter-process services.

Thearticle also looks at how to view information about running services on thedevice. Finally, it concludes by examining an implementation of a stock servicethat demonstrates several of the conceptual topics listed above, as well assome additional patterns useful in service development.

章节

第一节 - StartedServices

第二节- BoundServices

第三节查看Running服务和股票服务范例

结论

本文讲述XamarinAndroid的服务。讲述如何使用StartedService在后台执行长时间任务,以及BoundService给调用者提供接口。首先讲解与调用者同进程的本地服务的各种实现机制,在讲解远程服务如何使用Messenger类与客户端通信。最后,详细的股票服务展示如何实现同时为Started ServiceBound Service的服务。另外,范例还演示了使用警告来调度服务更新,以及使用通知和广播与用户通信。

This article covered using services inXamarin.Android. It discussed how Started Services can be used to do long runningwork in the background, as well as how Bound Services can be used to provide aservice interface to callers. After examining the various mechanisms forimplementing services locally within the process of the caller, remote serviceswere examined, showing how to use the Messenger classto communicate from a client. Finally, a detailed stock service example waspresented that showed how to implement a service as both a Started Service aswell as a Bound Service. In addition, the example covered how to use alarms toschedule service updates, as well as how to communicate to the user usingnotifications and broadcasts.

第一节 - Started Services

StartedService生命周期

服务的生命周期与调用它的组件如Activity是独立的。这种自制机制使服务在启动它的组件停止后还能继续执行。下图阐述了Started Service的生命周期,在总结小计中讲述每个方法的实现方式。

Services havea lifecycle that is separate from that of the component, such as an Activity,that starts them. This autonomy allows a service to continue running even afterthe starting component is no longer alive. The following figure illustrates thelifecycle of a started service, followed by a summary section that outlineswhat type of code would go in each method:


Service类包含了一个必须实现的纯抽象OnBind方法。在StartedService中可以简单的返回null,而在Bound Service中,必须返回IBinder实例用于客户端调用服务。将在下面解释。

The Service class also includes a method called OnBind that must be implemented because it is abstract. Itshould simply return null for a started service that is not also a boundservice. OnBind is used by bound services to return an IBinder instance that clients use to call a service. Thisprocedure will be examined later in this article.

回调方法的总结

上图是服务各个生命周期调用的各种方法。下面分别描述他们:

OnCreate—服务第一次启动时被调用。用于编写初始化代码。

OnStartCommand—启动服务的时候被调用,可调用StartService方法启动服务或系统自动重启服务。在这里启动长时间运行任务。方法返回StartCommmandResult类型的值,指示系统在内存缺少时服务被关闭,是否需要自动服务重启。这个调用在主进程中执行。

OnDestroy—服务调用StopSelfStopService时调用。这里可执行服务的结束过程。

The figureabove shows a variety of methods that are called at different points in thelifecycle of a service. The following list describes each of them:

·        OnCreate – Called one time when theservice is first started. This is where initialization code should beimplemented.

·        OnStartCommand – Called for each request tostart the service, either as a result of a call to StartService or a restart by the system. Thisis where the service can begin any long-running task. The method returns aStartCommmandResult value that indicates how or ifthe system should handle restarting the service after a shutdown due to lowmemory. This call takes place on the main thread.

·        OnDestroy – Called after the servicereceives a StopSelf or StopService call. This is where service-widecleanup can be performed.

Sticky(粘性)和非Sticky服务

当系统内存紧张时,Android可能会停止所有运行的服务。例外的情况是服务可显示的在前台启动。

当服务被系统停止,Android将根据OnStartCommand方法返回的值确定服务是否需要重启。StartCommandResult值如下:

Sticky—Sticky服务会重启,重启时OnStartCommandintent参数为null。用于连续不断执行长时间运行的操作,如更新股票信息。

RedeliverIntent 服务会重启,服务暂停前最后一个传递给OnStartCommandintent将传递进来。用于继续执行目录,如上传大文件。

NotSticky 服务不会自动重启。

StickyCompatibility API5及以后版本,效果同Sticky。但可用于API5之前版本。

When thesystem is under memory pressure, Android may stop any running services. Theexceptions to this rule are services explicitly started in the foreground,which are discussed later in this article.

When a service is stopped by the system, Android willuse the value returned from OnStartCommand to determine how or if the service should berestarted. This value is of type StartCommandResult, which can be any of the following:

·        Sticky – A sticky service will berestarted, and a null intent will be delivered

·        to OnStartCommand at restart. Used when theservice is continuously performing a long-running operation, such as updating astock feed.

·        RedeliverIntent – The service is restarted, andthe last intent that was delivered to OnStartCommand before the service was stoppedby the system is redelivered. Used to continue a long-running command, such asthe completion of a large file upload.

·        NotSticky – The service is notautomatically restarted.

·        StickyCompatibility – Restart will behave like Sticky on API level 5 or greater, butwill downgrade to pre-level 5 behavior on earlier versions.

Service类

概述

Service类是任意服务的基类。要创建服务,应用程序必须做如下过程:

必须创建一个Service的子类,重新前面讨论的声明周期方法实现服务。

类必须在AndroidManifest.xml中进行注册,在Xamarin Android中可以使用ServiceAttribute来修饰Service的子类。

另外,系统提供了Service子类IntentService,已经简单的实现了服务,但不能处理模拟器情况。本文将讨论IntentService类。

The Service class is the base class used for any service. Tocreate a service, an application must do the following:

·        Aclass must be created that inherits from Service, overriding the variouslifecycle methods described earlier that implement the service.

·        Theclass must be registered with the system by using the AndroidManifest.xml, which in Xamarin.Android canbe achieved by decorating the Service subclass with the ServiceAttribute.

Additionally, the system provides a Service subclass called IntentService, which simplifies service development at the cost ofnot being able to handle simultaneous requests. IntentService is discussed in detail later in this article.

实现服务

要实现服务,必须创建Service的子类,并使用自定义属性ServiceAttribute来修饰,如下所示:

To implement a service, a class must be created thatinherits from Service, and is adorned with theServiceAttribute custom attribute, as shown below:

[Service]
public class DemoService : Service
{
        …
}

这个属性将使服务在应用程序生成的时候注册到AndroidManifest.xml中。例如,将在AndroidManifest.xml中生成如下XML节点:

The attribute will result in the service beingregistered in the AndroidManifest.xml that is packaged with the application at build time.For example, an XML element like the following could also be added directly toanAndroidManifest.xml:

<service android:name="demoservice.DemoService"></service>

更多关于 AndroidManifest.xml的信息见 Working with AndroidManifest.xml文档。

注意服务运行在主线程中,因此长时间运行的工作应该开启独立线程。将在线程章节讨论。

Context类继承的其他组件如Activity,可以在需要的时候调用StartService方法启动服务。(对于调用远程服务方法,需要使用Bound服务,将在后续章节讲解)。例如,Activity中的如下代码将启动服务:

For more about the AndroidManifest.xml, see the Working with AndroidManifest.xml document.

It isimportant to note that the service will run on the main thread, so anylong-running work must be kept on a separate thread. This is demonstrated inthe threading section later in this article.

Another component that inherits from Context, such as an Activity, will start the service in the started servicescenario described here by calling the StartService method. (When performing remote procedure calls to aservice, a bound service is used. This process is described later in thisarticle.) For example, the following code in an Activity would start thisservice:

StartService (new Intent (this, typeof(DemoService)));

调用StartService将执行服务的OnStartCommand生命周期函数。实现OnStartCommand时必须返回一个StartCommandResult类型值,当由于内存紧缺导致服务停止时,这个值决定操作系统是否/如何重启服务。例如,返回StartResultCommand.Sticky,指示服务会自动重启,并传递intent参数为null

Calling StartService results in the OnStartCommand lifecycle method being called on the service. Theimplementation of OnStartCommand must return a StartCommandResult, which determines if the service will be restartedby the system, if it is shut down in a low memory situation. For example, thefollowing code returnsStartResultCommand.Sticky, which would result in the service being restartedautomatically with a null intent:

public override StartCommandResultOnStartCommand (Android.Content.Intent intent, StartCommandFlags flags, intstartId)
{
Log.Debug ("DemoService", "DemoService started");
 
        ...
 
returnStartCommandResult.Sticky;
}

为了节省系统资源,Started服务在完成长时间运行任务后需要调用StopSelf方法。这很重要,因为服务独立于启动他的组件运行。因此,即使启动他的组件如Activity已经释放,服务仍然可能运行。

例如,如下代码在执行完任务后调用StopSelf方法:

In order to conserve system resources, a startedservice implementation calls StopSelf after any long-running work is done. This isimportant because the service will run independently of the component thatcalledStartService. Therefore, the service will keep running even after thestarting component, such as an Activity, is destroyed.

For example, the following code calls the StopSelf method after it completes its work:

public void DoWork ()
{
var t = new Thread (() => {
Log.Debug ("DemoService", "Doing work");
Thread.Sleep (5000);
Log.Debug ("DemoService", "Work complete");
StopSelf ();
        }
        );
t.Start ();
}

另外,服务启动者可调用StopService方法停止服务:

Additionally, the caller can request that the servicebe stopped by calling the StopService method, as shown below:

StopService (new Intent (this, typeof(DemoService)));

Android在没有客户端绑定到服务的时候停止服务。绑定服务见后续文档。

Android willstop the service if there are no clients bound to it. Bound services arediscussed later in this article.

使用startId停止服务

服务可有多个调用者启动。如果有未完成的请求,可通过OnStartCommand传递的startId参数避免服务过早停止。startID与最近一次调用StartService相对应,每次调用都加1.因此,如果随后的请求还没有执行完OnStartCommand,服务可以调用StopSelfResult,传递最后一次接收到的startId的值(替换简单调用StopSelf)。如果StartService对应的OnStartCommand还没有执行完,系统就不会停止这个服务,因为用于调用StopSelfstartId参数并非最后调用StartService对应的值。

 

Multiple callers can request that a service bestarted. If there is an outstanding start request, the service can use thestartId that is passed into OnStartCommand to prevent the service from being stoppedprematurely. ThestartId will correspond to the latest call to StartService, and will be incremented each time it is called.Therefore, if a subsequent request to StartService has not yet resulted in a call to OnStartCommand, the service can call StopSelfResult, passing it the latest value of startId it has received (instead of simply callingStopSelf). If a call to StartService has not yet resulted in a corresponding call to OnStartCommand, the system will not stop the service, because the startId used in the StopSelf call will not correspond to the latestStartService call.

线程

服务运行在主线程,长时间运行的任务将阻塞主线程,导致应用程序反应缓慢。因此,服务中的一些代码应该在单独线程中执行。在Xamarin Android中,可使用System.Threading命名空间中的Thread类。例如,长时间运行的代码可运行在新的Thread类型中,如下所示:

A service runs on the main thread, so anylong-running task would block the main thread, making the applicationunresponsive. Therefore, such code should be implemented on a separate threadin the service. With Xamarin.Android, the threading classes from the System.Threading namespace are available for this scenario. Forexample, the long-running code can be run on a new Thread like this:

Thread t = new Thread (() => {
        // long running code ...
});
t.Start();

处理服务析构

服务停止时将执行OnDestroy方法。在这里可清理服务占用的资源。需要简单的重写OnDestroy方法,如下所示:

When a service is stopped, the OnDestroy method will be called on the service. This is thepart of the process where any cleanup of service-wide resources can be done. Toprovide an implementation, simply overrideOnDestroy as follows:

public override void OnDestroy ()
{
base.OnDestroy ();
        // cleanup code
}

使用IntentFilter启动服务

到目前为止,已经讲解了通过调用StartService(显式传递一个IntentService子类)来启动服务。这对于调用同一个应用程序内的服务是OK的。然而,服务还必须让其他应用程序来调用。无论调用本地或远处服务,都可使用Intent filter

同服务一样,Intent filter也注册在AndroidManifest.xml中。这个注册也可通过Xamarin Android的属性来完成。要为服务注册intent filter,服务类可通过IntentFilterAttribute来修饰。例如,如下代码中给服务添加叫做com.xamarin.DemoServiceintent filter

The discussion to this point has been about servicesthat are started by calling StartService with an explicit intent for the type of the Service subclass. This call is all that is needed if aservice is only to be used from within a single application. However, a servicecan also be made available in such a way that other applications can start it.To call a service in either the local or remote scenario, use intent filters.

Like the service itself, intent filters areregistered in the AndroidManifest.xml. This registration can also be accomplished withXamarin.Android by using attributes. To register an intent filter for aservice, the Service class is decorated with an IntentFilterAttribute. For example, the following code adds an intentfilter with an associated action of com.xamarin.DemoService:

[Service]
[IntentFilter(new String[]{"com.xamarin.DemoService"})]
public class DemoService : Service

同样,这个属性使AndroidManifest.xml文件添加了一个节点,随应用程序一起打包,内容如下:

Again, specifying this attribute results in an entrybeing included in the AndroidManifest.xml file, an entry that is packaged with the applicationin a way analogous to the following example:

<service android:name="demoservice.DemoService">
<intent-filter>
<action android:name="com.xamarin.DemoService" />
</intent-filter>
</service>

添加属性后,从Context继承的子类中可以启动服务,例如,Activity使用如下代码,传递intent filter

With this attribute in place, the service can bestarted from any class that inherits from Context, For example,Activity uses the following code, and passes the action to theintent:

StartService (new Intent ("com.xamarin.DemoService"));

同样,在Activity中如下代码可停止服务:

Likewise, the following code would stop the servicefrom an Activity:

StopService (new Intent ("com.xamarin.DemoService"));

通知用户

使用通知(Notification)

Startedservice用于长时间运行任务,但又不需要与用户交互的情况,运行在调用组件的生命周期之内。然而,使用通知可以在服务中与用户通信。Started service中的通知可用于长时间运行任务的情形,如大文件传输、完成,或提示用户特定事件。

如下代码将通知显示给用户,在通知窗口中显示信息:

Startedservices are used in scenarios where some long-running task is required thatboth does not need any user interaction and that needs to run beyond thelifetime of the calling component. However, by using notifications, informationfrom the service can be communicated to the user. Notifications on startedservices would be used in scenarios such as when a long-running task—like alarge file transfer—completes, or any case where an alert of some future eventneeds to be communicated.

For example,the following code displays a notification to the user, presenting a message inthe ticker and the notification screen:

varnMgr = (NotificationManager)GetSystemService (NotificationService);
var notification = new Notification (Resource.Drawable.Icon, "Message from demo service");
varpendingIntent = PendingIntent.GetActivity (this, 0, new Intent (this, typeof(DemoActivity)), 0);
notification.SetLatestEventInfo (this, "Demo Service Notification", "Message from demo service", pendingIntent);
nMgr.Notify (0, notification);

效果如下:

This coderesults in the following notification ticker:


当用户将通知窗口从上往下滑动,界面如下:

When the userslides down the notification screen from the top, the full notification isdisplayed:


通知中包含一个PendingIntent成员,封装与之相关的Intent。如果用户选择通知,PendingIntent中的Activity将被置于前台。

通知同时包含一个图标,是的Notification构造函数的第一个参数。可用于通知的图标大小件Status Bar Icons

The notification includes a PendingIntent, which is used to encapsulate an intent along withan action. If the user selects the notification, the Activity returned from the PendingIntent will be brought to the foreground.

The notification also includes an icon, which can bespecified in the first argument to the constructor of theNotification class. For details about the sizes that can be usedfor the notification icon see Status Bar Icons.

使用 Toast

通知机制适合于在发生重要的变更或需要输入时警告用户。这时因为通知向状态栏发生了临时信息,同时还有其他相关联信息。然而对于临时的被动的(不响应用户动作)消息,使用ToastToast类提供了一个临时弹出消息,如下所示:

Notifications are the preferred mechanism foralerting the user of an important change or the need for input. This is becausenotifications provide both a transient message in the status bar, as well asmore persistent information that can be both accessed and acted upon later, viathe notification screen. However, for messages that are both transient andpassive (“passive” meaning no user action can be taken on them), a Toast canalso be used. TheToast class provides a temporary pop-up containing amessage, as the following code illustrates:

Toast.MakeText (this, "The demo service has started", ToastLength.Long).Show();

界面如下所示:

This codeproduces the Toast in the screenshot shown below:


由于服务运行于主线程,可在OnStartCommand中直接调用Toast。然而,与Activity不同,服务没有RunOnUIThread 方法。在服务中可使用Handler实现相同效果。执行在独立线程中的服务代码可直接通过HandlerPost方法更新UI界面:

Since the service is running on the main thread, theToast can be displayed directly in the OnStartCommand method. However, unlike Activities, services don’thave a RunOnUIThread method. From services, a Handler can be used to achieve the same result. Any UIupdates that are made directly from code in a service that is running on aseparate thread can be performed in the Post method of a handler as follows:

varmyHandler = new Handler ();
...
myHandler.Post(() => {
Toast.MakeText (this, "Message from demo service", ToastLength.Long).Show();
});

除了Handle,还有其他可选的方法,包括Android.App.Application.SynchronizationContext.Post()System.Threading.SynchronizationContext.Current.Post()

In addition to a Handler, other options that may be used includeAndroid.App.Application.SynchronizationContext.Post() andSystem.Threading.SynchronizationContext.Current.Post().

接收广播

在服务中简单的通知用户发生的事件,可使用上面介绍的通知。而在Start Service中更新界面,可使用广播消息,Activity可使用广播接收器接收更新通知。

服务使用SendBroadcast 方法广播,传递与动作相关的IntentActivity实例化BroadcastReceiver的子类,将接收器注册为与服务广播动作相同的IntentFilter,在OnReceive方法中接收广播。使用BroadcastReceiver 的范例见本文后面的股票范例。

For simply notifying the user that something hashappened in the service, use notifications as shown earlier. For more elaborateUI updates from a started service, intents can be used to broadcastinformation, and an Activity can use a BroadcastReceiver to receiveupdates.

The service would send a broadcast by calling the SendBroadcast method, passing it an intent with an action. TheActivity would create an instance of a class that inherits from BroadcastReceiver, registering the receiver with anIntentFilter for the same action that the service used when itsent the broadcast. The receiver would then listen for the broadcast, receivingit in its OnReceive method. An example using a BroadcastReceiver is presented in the stock service example later inthis article.

前台服务

本文已经讨论了重启相关话题。这个行为可由StartCommandResult控制。必须恰当的处理由于内存压力导致的服务重启。然而,也可以创建在正常情况下不会释放的服务。这种服务叫做前台服务(foreground services)。用户能够感觉到前台服务的存在。例如网络广播服务就可以作为前台服务,因为用户通过声频播放就能感觉到服务的存在。

同时,不同于其他服务通知时可选的,前台服务需要通知来告诉用户服务在运行中。下面的代码运行在ServiceOnStartCommand中,将在其他一个前台服务:

Various restart behaviors were discussed earlier inthis article. These behaviors are controllable through theStartCommandResult. It’s important to handle service restarts properlywhen those restarts are triggered while the system is under memory pressure.However, it’s possible to create some services that will not be destroyed bythe system under normal circumstances. Services of this kind are known as foreground services. Users areactively aware of the presence of foreground services. For example, an Internetradio service is a good candidate to be created as a foreground service becausethe user is aware of its existence through the audio it plays back.

Also, unlike other services, where notifications areoptional, foreground services require notifications so that users will be awarethat the services are running. The following example, which would run insidethe Service (in theOnStartCommand method, for instance), shows how to request startinga service in the foreground:

var ongoing = new Notification (Resource.Drawable.Icon, "DemoService in foreground");
varpendingIntent = PendingIntent.GetActivity (this, 0, new Intent (this, typeof(DemoActivity)), 0);
ongoing.SetLatestEventInfo (this, "DemoService", "DemoService is running in the foreground", pendingIntent);
 
StartForeground ((int)NotificationFlags.ForegroundService, ongoing);

上面的代码中创建一个通知警告用户服务是运行在前台的。通知对象和NotificationFlags.ForegroundService标志一起传递给StartForeground 方法。

要将服务从前台移除,简单的调用StopForeground方法,传递一个Boolean值指示是否同时将通知也移除掉。StopForeground方法将服务从前台移除以便于在内存紧张的时候可以停止服务,但这个函数不会直接停止服务。

当服务停止时,如果通知还在显示,也会被移除。

In the above code, a notification is created thatwill alert the user that the service is running in the foreground. Thisnotification is then passed to the StartForeground method, along with theNotificationFlags.ForegroundService flag.

To remove the service from the foreground, simplycall StopForeground, passing a Boolean that indicates whether to removethe notification as well. StopForeground removes the service from the foreground so that thesystem will be able to stop it under memory pressure, but it does not stop theservice.

When theservice is stopped, if the notification is still present, it will be removed.

IntentService类

有些服务不需要并发调用。这种情况下,可使用IntentService 类。IntentService 是的Service子类,简单的实现了服务的各种生命周期方法。从IntentService 类继承只需要实现OnHandleIntent 方法。

IntentService 将所有的Intent请求加入到工作者队列中等待处理。每个Intent都会在一个独立的线程中按顺序处理,因此不会阻塞主线程,会在OnHandleIntent方法中传递Intent参数。当所有Intent处理完毕,IntentService 服务会调用StopSelf 方法结束自己。

Some services do not require multiple, simultaneouscallers. In such scenarios, the IntentService class can be used. IntentService is a Service subclass that simplifies service development byimplementing various lifecycle methods internally. Implementing an IntentService requires only that the OnHandleIntent method be implemented.

The IntentService works by sending all intents to a worker queue forprocessing. This queue processes each intent serially on a separate thread, soas to not block the main thread, passing the intent to the OnHandleIntentmethod. When all the intents have been processed, the IntentService stops itself by calling StopSelf internally.

实现IntentService.OnHandleIntent

下面代码中实现了IntentService 类中的OnHandleIntent 方法:

The following code shows an IntentService implementation with the overridden OnHandleIntent method:

[Service]
[IntentFilter(new String[]{"com.xamarin.DemoIntentService"})]
public class DemoIntentService: IntentService
{
publicDemoIntentService () : base("DemoIntentService")
        {
        }
 
protected override void OnHandleIntent (Android.Content.Intent intent)
        {
Console.WriteLine ("perform some long running work");
                ...
Console.WriteLine ("work complete");
        }
}

除了OnHandleIntent 方法,还需要在构造函数中向基类中传递一个字符串。这个字符串用来标识内部的工作线程;上面的代码提供的字符串是“DemoIntentService”。这个代码与其他服务一样使用属性注册IntentService服务。

另外,调用代码与上面所述的一样调用StartService方法,传递一个intent参数。每次调用StartService,都会将其intent参数放在队列中,按顺序由OnHandleIntent方法处理。

OnHandleIntent方法中的代码不会阻塞主线程,因为会在独立线程中调用。

IntentService类可以极大简化服务开发,可用于无需多用户、并发的情况下。

Other than the OnHandleIntent method, the only additional requirement is that theconstructor needs to pass a string to the base class. This string provides aname that is used to identify the worker thread internally; the code abovesupplies the string “DemoIntentService.” The code to register the IntentService uses the same attributes as any other service.

Additionally, the calling code would call StartService, passing an intent as shown earlier in the article.Each timeStartService is called, the intent would be queued and forwarded to OnHandleIntent in the order received.

The implementation in OnHandleIntent does not block because it is called on a separatethread from the main thread.

The IntentService class greatly simplifies service development andshould be used in any scenario where multiple, simultaneous service calls arenot required.

第二部分- Bound Services

除了后台运行,服务也可以提供客户端-服务器接口,让客户与之交互。这样的服务叫做Bound ServicesBoundService可以在本地进程中创建,也可以为多个应用程序提供服务而创建在远程进程中。本地服务为应用程序提供后台运行特定函数的能力。而远程服务提供了系统级别的跨进程处理的能力。

In addition to running indefinitely in thebackground, services can also provide a client-server interface that a clientcan interact with. Such services are termed BoundServices. Bound Services can be created either locally in aparticular application process, or in a remote process that can servicemultiple applications. Local services would be used to provide backgroundworker capability within an application process for particular method calls.Remote services, however, would be used across process boundaries to provide somesystem-wide capability.

Bound Service生命周期

Bound Service的生命周期与StartedService不同。Bound Service启动很有规律。当一个客户端请求连接的时候会创建服务,所有客户端断开连接时释放。然而,Bound Service也可以与StartedService一样,实现为Service的子类,如果由startService启动,则所有客户端断开连接后也不会释放。同样,如果还存在客户端连接,在调用了StopServiceStopSelf后,也不会释放。

下图阐述了Bound Service生命周期:

The lifecycle of a Bound Service is different fromthat of a Started Service. Unlike Started Services, Bound Services do not runindefinitely. Instead, they are created when a client connects to them and aredestroyed after all bound clients have disconnected. However, a Bound Servicecan be implemented in the same Service subclass as a Started Service, so if it has beenstarted with a StartService call, it will not be destroyed even after all boundclients have disconnected. Likewise a StartedService will not be destroyed after a call to StopService orStopSelf, if it still has bound clients connected.

The followingdiagram illustrates the lifecycle of a Bound Service:


OnBind 这个方法会返回IBinder实例,客户端可从中获取服务实例,调用服务的方法。

OnUnbind 当所有客户端都断开连接时会被调用。将返回值设置为true,当有客户端在连接过来的时候,会使用其intent参数做为参数调用OnRebind方法。如果当服务以及没有客户端绑定的情况下还要继续运行,就需要这么做。这将使在未调用StopServiceStopSelf时,无客户端绑定的服务变为Started Service。这时可在OnRebind方法中获取intent参数。默认返回false

·        OnBind – This method is used to returnan instance on an IBinder that the client uses to obtain aservice instance that can, in turn, call methods on the service.

·        OnUnbind – This method is called when allbound clients have unbound. By returning true from this method, the servicewill later call OnRebind with the intent passed to OnUnbind when new clients bind to it. Youwould do this when a service continues running after it has been unbound. Thiswould happen if the recently unbound service were also a started service, and StopService or StopSelf hadn’t been called. In such ascenario,OnRebind allows the intent to beretrieved. The default returns false, which does nothing.

实现本地Bound Service

实现本地Bound Service需要如下步骤:

1.        创建返回服务实例的Binder子类,Binder实现了IBinder

2.        实现返回Binder子类实例的OnBind方法。

Binder子类负责向客户端返回服务实例,以便于调用服务方法。OnBind方法返回Binder实例。客户端要连接到服务的时候会调用OnBind方法。

例如,下面代码实现了Binder子类DemoServiceBinder

Implementing aLocal Bound Service requires the following:

1.       A subclass of the Binder class that returns the serviceinstance, where Binder is the default implementation of IBinder.

2.      Animplementation of the OnBind method that returns an instanceof the Binder subclass.

The Binder subclass is responsible for returning the serviceinstance to clients so that they can make method calls against it. The OnBind method returns an instance of the BinderOnBind is called by Android the first time any clientattempts to connect to a service.

For example, the following code shows animplementation of a Binder subclass named DemoServiceBinder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

publicclassDemoServiceBinder : Binder
{
DemoService service;
 
publicDemoServiceBinder (DemoService service)
        {
this.service= service;
        }
 
publicDemoServiceGetDemoService ()
        {
return service;
        }
}

客户端可使用DemoServiceBinder 类获取DemoService ,这样客户端就可以调用服务方法了。在DemoService类的OnBind方法中需要返回DemoServiceBinder类的实例。下面代码是DemoServiceBinder类的OnBind方法的实现:

Clients can use the DemoServiceBinder class to obtain a reference to the DemoService itself, which the client can then use to call theservice’s methods. The mechanism that the DemoService uses to return the instance ofDemoServiceBinder is the OnBind lifecycle method. The following code shows theimplementation of OnBindthat returns a DemoServiceBinder:

1
2
3
4
5
6

publicoverrideIBinderOnBind (Intent intent)
{
        binder =newDemoServiceBinder (this);
return binder;
}

binder实例变量中有对DemoService的引用。由于OnBind尽在客户端第一次连接到服务的时候调用,后续会重用这个binder实例。

The binder variable is an instance variable for which the DemoService keeps a reference. Since the OnBindmethod is only called on the first connection to theservice, additional calls will reuse the same binder instance.

客户端服务绑定

客户端调用BindService方法,传递一个intent和一个ServiceConnect实例,连接到服务上。ServiceConnection是一个在客户端和服务间提供调用接口的类。例如,如下代码在ActivityOnStart方法中绑定一个intent filtercom.xamarin.DemoService的服务:

A client binds to a service by calling BindService with an intent and an instance of a ServiceConnection. AServiceConnection is a class that provides a calling interface betweenthe client and the service. For example, the following code in the OnStart of an Activity binds a service that has an intentfilter with the actioncom.xamarin.DemoService:

1
2
3
4
5
6
7
8
9

Protected override void OnStart ()
{
base.OnStart ();
 
var demoServiceIntent = newIntent ("com.xamarin.DemoService");
demoServiceConnection = new DemoServiceConnection (this);
BindService (demoServiceIntent, demoServiceConnection, Bind.AutoCreate);
}

上面的代码中在BindService函数中传递了Bind.AutoCreate 参数值,如果绑定成功将自动创建服务。BindService是异步调用的,如果不存在可绑定的服务则返回false,或连接成功的时候触发ServiceConnection 类的OnServiceConnected 函数。

上面代码还创建了DemoServiceConnection实例。这是ServiceConnection的子类,并重写了OnServiceConnected 方法获取IBinder的引用。本例中IBinder实例是DemoServiceBinder类型的。DemoServiceBinder实例可获取DemoService实例,这样客户端就可以调用服务的方法了。

DemoServiceConnection 类定义如下:

The above code uses the Bind.AutoCreate value in the BindService call to automatically create the service, if thebinding exists. BindService itself is an asynchronous call that will eitherreturn false if there is no service to bind to, or cause acallback to be sent to the OnServiceConnected method of a ServiceConnection class when the connection is made.

The code also creates an instance of DemoServiceConnection. This is a class that inherits fromServiceConnection and overrides OnServiceConnected to get a reference to the particular IBinder for the service. In this case, IBinder is an instance of a DemoServiceBinder. The DemoServiceBinder is used to get a reference to the DemoService itself, so that the client can call any methods thatthe service defines.

The following code shows the DemoServiceConnection class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

classDemoServiceConnection : Java.Lang.Object, IServiceConnection
{
DemoActivity activity;
 
publicDemoServiceConnection (DemoActivity activity)
        {
this.activity= activity;
        }
 
publicvoidOnServiceConnected (ComponentName name, IBinder service)
        {
vardemoServiceBinder= service asDemoServiceBinder;
if (demoServiceBinder!=null) {
activity.binder=demoServiceBinder;
activity.isBound=true;
                }
        }
 
publicvoidOnServiceDisconnected (ComponentName name)
        {
activity.isBound=false;
        }
}

注意构造函数中传递了一个Activity实例参数,以便于在OnServiceConnected回调方法中获取Activity实例,并设置Activitybinder属性。这是特意安排的流程,使在Activity中使用其binder属性就可以获取到服务的引用,以便于调用服务方法。

Notice that an instance of the Activity is passed into the constructor so that the binderobtained in theOnServiceConnected callback can be set on the Activity itself. This is the order of the process because it’stheActivity that wants to use the binder to get a reference tothe service so that the Activity can call the service’s methods.

调用服务方法

例如假设服务定义了一个GetText方法。点那个OnServiceConnected被调用而且Activity设置了binder属性,Activity就可以使用binder获取服务实例并调用GetText方法了:

For example, say the service defines a method called GetText. Once OnServiceConnected has been called and theActivity has the binder, the Activity can use the binder toget the service reference and subsequently callGetText as follows:

1
2

binder.GetDemoService ().GetText ();

解除服务绑定

客户端如Activity在使用完服务后必须解除绑定。这样可使服务在无请求的时候释放自己。当断开服务连接,ServiceConnect类的OnServiceDisconnected方法将被调用。

要解除位于服务的绑定,客户端可调用UnbindService,以绑定时的ServiceConnection 实例作为参数。例如,下面代码展示ActivityOnDestroy方法中解除与服务的绑定:

Clients such as the Activity shown here also must unbind from the service whenthey are finished using it. This allows the service to shut down when it is notin use. After the service disconnects, the OnServiceDisconnectedmethod of ServiceConnection class will be called.

To unbind from a service, a client calls UnbindService, passing the ServiceConnection instance it used in the binding. For example, thefollowing code shows an Activity unbinding from a service in the OnDestroy method of the Activity:

1
2
3
4
5
6
7
8
9
10

protectedoverridevoidOnDestroy ()
{
base.OnDestroy ();
 
if (isBound) {
UnbindService (demoServiceConnection);
isBound=false;
        }
}

如果没有未返回的StartService调用,也没有客户端绑定到服务上,Android将会关闭服务。然而,当配置变更时(如旋转设备)会调用OnStop方法,这时上面的代码将解除服务绑定。如何保持对服务的绑定将在下节讨论。

If there are no outstanding calls to StartService, and no other clients are bound to the service,Android will shut down the service. However, since OnStop is called during a configuration change (such as arotation change), the above code would also unbind from the service in thatscenario. How to preserve the binding to the service in this scenario isdiscussed next.

处理配置变更

当配置变更(如旋转设备)时,所有Activity生命周期函数中(如OnPauseOnStop)与服务的绑定和解除绑定代码都会执行。如果绑定到服务是很费时的,可以保存ServiceConnection。为绑定到服务的客户端在配置变更后保存ServiceConnection

应该在ApplicationContext 中调用BindService,而不是在Activity

nRetainNonConfigurationInstance 方法中返回SerivceConnection 实例

nRetainNonConfigurationInstance 方法应该设置一个标志位,当配置变更并没有导致服务停止,就不需要绑定服务。

When a configuration change such as a device rotationoccurs, any binding and unbinding code that is bound to an Activity and placedin a lifecycle method (such as OnPause or OnStop) will be run. If binding to a particular service isexpensive, you may want to preserve the ServiceConnection. For a client bound to a service to preserve theServiceConnection across configuration changes:

·        The BindService method should be called from the ApplicationContext rather than from the Activity.

·        The SerivceConnection instance should be returned from OnRetainNonConfigurationInstance.

·        The OnRetainNonConfigurationInstance method should set a flag thatwill only be used to unbind the service when the service is not stopped due toa configuration change.

上面代码应改为:

For the code above, the BindService call would become:

1
2

ApplicationContext.BindService (demoServiceIntent, demoServiceConnection, Bind.AutoCreate);

当配置变更时DemoServiceConnection应该保存IBinder 实例的引用:

Then the DemoServiceConnection would keep a reference to the IBinder for retrieval after the configuration change:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

classDemoServiceConnection : Java.Lang.Object, IServiceConnection
{
DemoServiceBinder binder;
 
publicDemoServiceBinderBinder {
get {
return binder;
                }
        }
        ...
publicvoidOnServiceConnected (ComponentName name, IBinder service)
        {
vardemoServiceBinder= service asDemoServiceBinder;
if (demoServiceBinder!=null) {
                        ...
// keep instance for preservation across configuration changes
this.binder= (DemoServiceBinder)service;
                }
        }

接着OnRetainNonConfiguration 方法应该设置一个标志位,在Activity中将其初始化为false。接下来将阻止由于配置变更而释放绑定,并返回DemoServiceConnection实例:

Next, the OnRetainNonConfiguration method would set a flag, initialized to false when declared in the Activity. This method would thenbe used to prevent unbinding due to a configuration change, and would returntheDemoServiceConnection instance:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
224

Bool isConfigurationChange=false;
...
// return the service connection if there is a configuration change
publicoverrideJava.Lang.ObjectOnRetainNonConfigurationInstance ()
{
base.OnRetainNonConfigurationInstance ();
 
isConfigurationChange=true;
 
returndemoServiceConnection;
}
 
protectedoverridevoidOnDestroy ()
{
base.OnDestroy ();
 
if (!isConfigurationChange) {
if (isBound) {
UnbindService (demoServiceConnection);
isBound=false;
                }
        }
}

最后,在ActivityOnCreate方法中,从LastNonConfigurationInstance中获取DemoServiceConnection DemoServiceBinder的实例:

Finally, in the OnCreate method of the Activity, the DemoServiceConnection instance and theDemoServiceBinder would be retrieved from the LastNonConfigurationInstance:

1
2
3


6

// restore from connection there was a configuration change, such as a device rotation
demoServiceConnection=LastNonConfigurationInstanceasDemoServiceConnection;
 
if(demoServiceConnection!=null)
        binder =demoServiceConnection.Binder;

更多与旋转Activity相关内容见Handling Rotation

For more information about how to work with rotationin Activities, see Handling Rotation.

服务的进程间通信

除了本地服务(与调用者在同一个进程),服务也可以在其自己的进程内创建。要在Android中执行于服务的进程间通信(IPCinter-process communication),可使用Messenger 类以客户端服务器的方式发生消息。与服务进行IPC通信的更高级技术(AndroidInterface Definition Language (AIDL)),将在XamarinAndroid的后续发布版中提供。

In addition to local services, which run in the sameprocess as the caller, services can also be created in their own processes. Toperform inter-processcommunication (IPC) toa service on Android, a Messenger class can be used to send messages in a client-serverfashion. A more advanced technique that can be employed in order to perform IPCwith services is to use AndroidInterface Definition Language (AIDL), which Xamarin.Android willsupport in a future release.

使用Messager类

Android提供了Messager类无需AIDL支持就可以与服务进行IPC通信。当使用Messager,客户端发生的消息将放置在服务的队列中,依次进行处理。同样,Messager也不会向客户端暴漏服务的接口。客户端发现Message对象,服务使用Handler类进行处理。

Android provides the Messenger class to enable IPC for services without using AIDL.When using Messenger, messages sent from clients are queued in theservice and processed one at a time. Also, the Messenger does not expose a service interface to clients.Instead, clients send Message objects, which the service handles in a Handlerclass.

实现Messenger-BoundService

要实现使用Messager技术的服务,需要在服务中创建一个Messager。服务端应该做两件事:

1.        创建一个Handler子类

2.        使用Handler实例作为参数,创建Messager实例

例如,如下代码中的服务,使用DemoHandler类的实例创建命名为demoMessenger Messager

Implementing a service that uses a Messenger involves creating a Messenger in the service that returns a binder to the client.Two things need to be in place in the service for this:

1.       A class that inherits from Handler.

2.      Messenger instance that is created with aninstance of the Handler.

For example, the following code shows a service thatuses a Messenger instance named demoMessenger that, in turn, takes an instance of a class called DemoHandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
9

[Service]
[IntentFilter(newString[]{"com.xamarin.DemoMessengerService"})]
publicclassDemoMessengerService : Service
{
Messenger  demoMessenger;
 
publicDemoMessengerService ()
        {
demoMessenger=newMessenger (new DemoHandler ());
        }
 
publicoverrideIBinderOnBind (Intent intent)
        {
returndemoMessenger.Binder;
        }
 
classDemoHandler : Handler
        {
publicoverridevoidHandleMessage (Messagemsg)
                {
                        ...
                }
        }
}

当客户端绑定到服务,并向服务发生含有Message对象的消息时调用HandleMessage方法。

为了简单,这里的Message对象包含两个整数属性,命名为Arg1Arg2,还有一个命名为What的属性用来区分其他消息调用。例如,基于上面的两个属性,客户端可以发送不同的What值,使HandleMessag的实现执行不同的动作。

要传递更多类型数据,Message有一个Android.OS.Bundle类型的Data属性。这个属性可以使用键值对传递更多的值。例如,如果要在Data中包含一个叫做keyInputText的值,可在服务的HandleMessage中按如下方式获取:

The HandleMessage method is called when a client binds to the serviceand sends a message to the service that is contained in a Message object.

For simple messages, the Message object can contain two integer properties, named Arg1 and Arg2 respectively, as well as a property called What that can be used to distinguish the particularmessage call. For instance, based upon the values of these two properties, theclient could send different values of What and the HandleMessageimplementation and so take different actions.

To send more types of data than integers, Message includes a Data property that is an Andorid.OS.Bundle. This property can include other values that can beretrieved by key. For example, if the client included a string with the key 'InputText' in the Data, this string could be retrieved in the HandleMessage method in the service as shown below:

1
2
3
4
5
6
7
8
9
10

classStockHandler : Handler
{
publicoverridevoidHandleMessage (Messagemsg)
        {
Log.Debug ("DemoMessengerService", "What = "+msg.What.ToString());
string text =msg.Data.GetString ("InputText");
Log.Debug ("DemoMessengerService", "InputText = "+ text);
        }
}

在客户端调用Messager服务

要使用Messager调用服务,客户端需要创建Message对象,然后调用MessagerSend方法。客户端需要:

1.        实现创建MessagerIServiceConnection 接口

2.        创建Message对象并添加数据

3.        调用MessagerSend方法

To use a Messenger to call a service, a client needs to create a Message object, and then call the Send method of the Messenger class. The client needs to:

1.       Implement an IServiceConnection that creates a Messenger.

2.      Createa Message object and add data to it.

3.      Callthe Send method of the Messenger.

在客户端创建Messenger

客户端在连接到服务的时候(IServiceConnection 实现类中完成,客户端调用BindService)创建Messager。例如,下面代码中客户端连接到服务,然后创建Messager实例:

The client creates the Messenger when it connects to the service. In the IServiceConnection implementation, this occurs after the client calls BindService. For example, the following code shows a clientconnecting to a service, and then creating a Messenger instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

protectedoverridevoidOnStart ()
{
base.OnStart ();
 
vardemoServiceIntent=newIntent ("com.xamarin.DemoMessengerService");
demoServiceConnection=newDemoServiceConnection(this);
BindService (demoServiceIntent, demoServiceConnection, Bind.AutoCreate);
}
 
protectedoverridevoidOnStop ()
{
base.OnStop ();
 
if (isBound) {
UnbindService (demoServiceConnection);
isBound=false;
        }
}
 
classDemoServiceConnection : Java.Lang.Object, IServiceConnection
{
DemoMessengerActivity activity;
 
publicDemoServiceConnection (Activity1 activity)
        {
this.activity= activity;
        }
 
publicvoidOnServiceConnected (ComponentName name, IBinder service)
        {
activity.demoMessenger=newMessenger (service);
activity.isBound=true;
        }
 
publicvoidOnServiceDisconnected (ComponentName name)
        {
activity.demoMessenger.Dispose ();
activity.demoMessenger=null;
activity.isBound=false;
        }
}

这个过程与上面展示的本地Bound Service很相似。主要的不同在于在OnServiceConnected方法中使用IBinder创建Messager实例。

This procedure is very similar to the local boundservice example shown earlier. The main difference is that the code here, in OnServiceConnected, creates a Messenger from the IBinder, which it can then use to call the service.

创建消息(Message)

使用Message.Obtain方法创建消息。Message.Data属性是Android.OS.Bundle类的,可设置大量信息。例如,下面代码创建一个Message并包含一个键为InputText的值:

To create a Message, use the Message.Obtain method. The Message.Data property allows anAndroid.OS.Bundle to be sent in the Message, as mentioned earlier. The bundle can be used toinclude additional data in the Message. For example, the following code creates a Message and includes a string with the key“InputText” to a bundle:

1
2
3
4
5

Messagemessage=Message.Obtain ();
Bundle b =newBundle ();
b.PutString ("InputText", "text from client");
message.Data= b;

Message发送到服务

使用在IBinder OnServiceConnected方法中创建的Messager实例向服务端发送消息。如下所示:

To send the Message to the service, the Messenger instance created from the IBinder in OnServiceConnectedis used. Simply call the Send method of the Messenger, as shown below:

1
2

demoMessenger.Send (message);

服务将在Handler类中接收到Message,并提取数据。

The server will receive the Message in the Handler, where it can extract the data, as shown earlier.

 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
相关说明: ConsoleApplication1_sERVER ----PC-服务端 MyNetTest --------------------IOS-客户端 1、PC-服务端 只是一个运行在windows系统下的 控制台程序。接收来自客户端的信息。 2、IOS-客户端 运行在ios模拟器上,连接PC服务端的ip,发生相关信息。 3、两台主机,一台是运行windows系统的计算机。另外一台是MacBook计算机。运行ios模拟器。 4、pc-服务端,可以用vs2008打开并且编辑。 5、ios客户端,使用的是XamarinStudio 打开并且编辑。 以下是这个例子中的特别提到的地方 A、这个例子是完全用C#写的。 B、ios由于是伪后台,当程序退回到后台,系统留给程序的可运行时间就只有3分钟。 过了3分钟,就会把这个程序的所有线程挂起(当然内部预留了长任务运行这一后招)。 经过多次试验后,可以借助着3分钟的长任务运行,然后通过简单的修改来突破这个界限。 在本例子中,所有线程共享一个线程ID。 其中只需要一条线程负责不停的延长这个线程ID的运行时间,然后其他的线程就只需要专注于其本应该要做的任务即可。 C、本例子中,还实现了另外一个功能,就是ios程序与PC程序进行socket。当然是最为简单的。 ---------------- 由于本人也是刚刚使用c#开发ios程序,初入门,为了这两个问题,也是研究了很久,可查的资料又很少。 希望能够帮助到同样遇到困难的你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值