【Android】17.3 Activity与StartedService的绑定
分类:C#、Android、VS2015;
创建日期:2016-03-03
一、简介
除了上一节介绍的基本绑定以外,你还可以创建一个同时支持started和bound的服务。也就是说,服务可以通过调用 StartService() 来启动,这会使它一直保持运行,同时它也允许客户端通过调用BindService() 来与之绑定。
虽然你通常应该要实现 OnBind() 或 OnStartCommand() 中的一个,但有时需要同时实现两者。比如,音乐播放器的服务也许就需要同时实现后台运行和支持绑定。这样,activity就可以启动服务来播放音乐,并且音乐会一直播放下去,即使用户离开该应用程序也没关系,这个activity可以绑定播放服务来重新获得播放控制权。
另外,如果你的服务是started和bound的,那么服务启动后,系统将不会在所有客户端解除绑定时销毁它。取而代之的是,你必须通过调用StopSelf() 或 StopService() 显式终止此服务。
服务只在为绑定的应用程序组件工作时才会存活,因此,只要没有组件绑定到服务,系统就会自动销毁服务(你不需要像started服务中那样通过OnStartCommand()来终止一个bound服务)。
一旦OnBind方法返回绑定的实例,Android就会引发实现IServiceConnection接口的对象的ServiceConnected事件,然后,客户端就可以通过Binder引用它。
多个客户端可以同时连接到同一个服务上。不过,只有在第一个客户端绑定时,系统才会调用服务的 OnBind() 方法来获取 IBinder 。然后,系统会向后续请求绑定的客户端传送这同一个 IBinder ,而不再调用 OnBind() 。当最后一个客户端解除绑定后,系统会自动销毁服务(除非服务是同时通过 StartService() 启动的)。
二、示例2运行截图
该例子演示如何绑定到一个started服务。
单击【启动服务】按钮后,就会在左上角出现一个通知图标,下拉展开该图标,就可以看到来自服务的通知信息。
单击【调用服务中提供的方法】按钮,会显示调用服务中对应方法返回的结果,单击【停止服务】按钮,左上角的通知图标也会同时消失。
三、主要设计步骤
1、添加ch1702_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:id="@+id/ch1702_startService" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="启动服务" /> <Button android:id="@+id/ch1702_callService" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="调用服务中提供的方法" /> <Button android:id="@+id/ch1702_stopService" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="停止服务" /> </LinearLayout>
2、添加ch1702Service.cs
using Android.App; using Android.Content; using Android.OS; using Android.Widget; using System.Threading; namespace MyDemos.SrcDemos { [Service] [IntentFilter(new string[] { action })] public class ch1702Service : Service { public const string action = "ServiceDemo.ch1702Service"; ch1702ServiceBinder binder; public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) { Notification notification = GetNotification(); //启动前台任务 StartForeground((int)NotificationFlags.ForegroundService, notification); var t = new Thread(() => { //发送通知 var m = (NotificationManager)GetSystemService(NotificationService); m.Notify((int)NotificationFlags.ForegroundService, notification); Thread.Sleep(50000); //延时50秒模拟长时间运行的服务 //停止前台任务 StopForeground(true); //停止后台服务 StopSelf(); }); t.Start(); return StartCommandResult.NotSticky; } private Notification GetNotification() { var pendingIntent = PendingIntent.GetActivity(this, (int)NotificationFlags.ForegroundService, new Intent(this, typeof(MainActivity)), PendingIntentFlags.UpdateCurrent); Notification.Builder builder = new Notification.Builder(this) .SetContentTitle("来自ch1702Service的通知") .SetContentText("正在前台运行ch1702Service") .SetContentIntent(pendingIntent) .SetSmallIcon(Resource.Drawable.Icon); Notification notification = builder.Build(); return notification; } public override IBinder OnBind(Android.Content.Intent intent) { binder = new ch1702ServiceBinder(this); return binder; } public string GetText() { return "这是来自ch1702Service的信息"; } public override void OnDestroy() { var myHandler = new Handler(); myHandler.Post(() => { Toast.MakeText(this, "正在停止ch1702Service", ToastLength.Long).Show(); }); base.OnDestroy(); } } public class ch1702ServiceBinder : Binder { ch1702Service service; public ch1702ServiceBinder(ch1702Service service) { this.service = service; } public ch1702Service GetMyService() { return service; } } }
3、添加ch1702MainActivity.cs
using Android.App; using Android.Content; using Android.OS; using Android.Widget; using Android.Content.Res; namespace MyDemos.SrcDemos { [Activity(Label = "ch1702MainActivity")] public class ch1702MainActivity : Activity { bool isBound = false; bool isConfigurationChange = false; ch1702ServiceBinder binder; ch1702ServiceConnection myServiceConnection; Intent serviceIntent; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.ch1702_main); var start = FindViewById<Button>(Resource.Id.ch1702_startService); start.Click += delegate { serviceIntent = new Intent(ch1702Service.action); StartService(serviceIntent); }; var callService = FindViewById<Button>(Resource.Id.ch1702_callService); callService.Click += delegate { if (isBound) { RunOnUiThread(() => { string text = binder.GetMyService().GetText(); Toast.MakeText(this, text, ToastLength.Short).Show(); }); } }; var stop = FindViewById<Button>(Resource.Id.ch1702_stopService); stop.Click += delegate { StopService(serviceIntent); if (isBound) { UnbindService(myServiceConnection); isBound = false; } }; // 如果配置改变了(比如旋转了屏幕),则恢复连接 if (myServiceConnection != null) { binder = myServiceConnection.Binder; } } protected override void OnStart() { base.OnStart(); var myServiceIntent = new Intent(ch1702Service.action); myServiceConnection = new ch1702ServiceConnection(this); BindService(myServiceIntent, myServiceConnection, Bind.AutoCreate); } public override void OnConfigurationChanged(Configuration newConfig) { isConfigurationChange = true; base.OnConfigurationChanged(newConfig); } protected override void OnDestroy() { base.OnDestroy(); if (!isConfigurationChange) { if (isBound) { UnbindService(myServiceConnection); isBound = false; } } } private class ch1702ServiceConnection : Java.Lang.Object, IServiceConnection { private ch1702MainActivity activity; public ch1702ServiceBinder Binder { get; private set; } public ch1702ServiceConnection(ch1702MainActivity activity) { this.activity = activity; } public void OnServiceConnected(ComponentName name, IBinder service) { var myServiceBinder = service as ch1702ServiceBinder; if (myServiceBinder != null) { activity.binder = myServiceBinder; activity.isBound = true; // 即使配置发生了改变,仍保持绑定到该实例 Binder = myServiceBinder; } } public void OnServiceDisconnected(ComponentName name) { activity.isBound = false; } } } }