转载请标明出处:http://blog.csdn.net/goldenfish1919/article/details/40395515
原文:http://developer.android.com/guide/components/bound-services.html
bound service是工作在客户端/服务端模式的service。bound service允许别的组件绑定到service上,然后发送请求、接收响应、甚至是做跨进程的IPC操作。bound service只有在它为别的组件提供服务的时候才是存活的,并且不会在后台无限的运行下去。
本文档会告诉你如何来创建一个bound service,包括如何从别的组件绑定到service上。但是,你首先要参考Service的文档来获取关于service的一些知识,比如如何在service中发送提醒,如何让service在前台运行,等等。
bound service基础
bound service是service的一种实现,它允许别的组件绑定到它上面并和它做交互。为了给service提供绑定功能,你必须要实现onBind()回调。这个方法会返回一个IBinder对象,它就定义了客户端可以使用的跟服务端就行交互的接口。
客户端可以通过调用bindService()绑定到service上。当客户端想绑定的时候,它必须要提供一个ServiceConnection,ServiceConnection会监控跟service的连接。bindService()方法必须要立即返回而且没有返回值,但是,android系统会调用ServiceConnection的onServiceConnected()来创建service和客户端之间的连接,然后传递进去客户端可以用来跟服务端通信的IBinder。
多个客户端可以同时连接到一个service上,但是,只有当第一个客户端绑定到service的时候,系统才会去调用service的onBind()方法来检索出IBinder对象,后续客户端绑定的时候,系统并不会再次调用onBind(),而是把检索出的那个IBinder对象传递过去。
当最后一个客户端从service解绑以后,系统会销毁掉service(除非service同时也是用startService()启动的)
当你实现你的bound service的时候,最重要的事情就是定义onBind()回调中返回的IBinder接口。有几种不同的定义service的IBinder接口的方式,下面的章节将会介绍这几种技术。
如何创建Bound Service
当创建一个提供绑定功能的service的时候,必须要给客户端提供一个可以跟service进行交互的编程接口,有三种定义接口的方式:
继承Binder类
如果service是你的应用私有的,并且是和客户端运行在同一个进程中(典型的情况),你应该通过继承Binder类来创建接口,然后在onBind()中返回它的实例。客户端收到Binder以后,可以直接使用它访问Binder的public方法甚至是service的方法。
当service仅仅是你自己应用的一个后台工作者的情况下,这是比较合适的方式。之所以不用这种方式的唯一的原因是因为你的service要被别的应用或者是跨进程来使用。
使用Messenger
如果你的接口要跨进程使用,可以用Messenger来创建service的接口。这种方式下,service定义了一个Handler来处理不同类型的Message对象。Handler是Messenger的基础,它可以在多个客户端之间共享同一个IBinder,允许客户端使用Message给service发送请求。此外,客户端可以定义它自己的Messenger,然后service就可以把message发送给客户端。
这是最简单的实现IPC的方式,因为Messenger会把所有的请求入队到同一个线程中,所以不需要把你的service设计成线程安全的。
使用AIDL
AIDL
AIDL可以将对象序列化成操作系统可以理解的基本数据类型然后反序列化到别的进程中来做IPC。前面介绍的Messenger实际上底层结构也是基于AIDL的。正如前面提到的那样,Messenger会把客户端的请求压到同一个线程中,所以service一次只能接受到一个请求。如果你想让你的service可以同时并发的处理多个请求,那么就可以使用AIDL。在这种情况下,service就必须要能够安全的支持多线程。
要直接使用AIDL,必须要创建一个定义了编程接口的.aidl文件。Android SDK工具会使用这个文件来产生一个实现了这些接口的抽象类来处理IPC,要在你的service中对这个抽象类做实现。
注意:大多数应用不应该使用AIDL来创建bound service,因为它需要多线程支持并且会导致非常复杂的实现。基于此,对于大多数应用来说AIDL都不适用,因此本文档不会介绍如何在service中来使用它。如果你确定你需要直接使用AIDL,参考AIDL的文档(http://developer.android.com/guide/components/aidl.html)。
继承Binder类
如果你的service只是在本应用使用而且不需要跨进程的操作,那么你可以实现你自己的Binder类,它可以让客户端直接访问service的公开的方法。
注意:只有当客户端和service是处于同一个应用的同一个进程中的时候才可以这么用,大多数情况下也正是如此。比如,音乐应用就和适合这种方式,因为它需要把Activity绑定到在后台播放音乐的它自己的service上。
下面是具体的步骤:
(1)在你的service中,创建Binder的实例:
要么包含客户端可以调用的public方法
要么返回当前的service实例,它有客户端可以调用的public方法
要么返回service内部的其他类的实例,这个类也有客户端可以调用的public方法。
(2)在onBind()回调中返回Binder的实例。
(3)客户端从onServiceConnected()回调中接收Binder实例,使用Binder提供的方法调用bound service。
注意:service和客户端必须在同一个应用的原因是客户端可以正确的转化返回的对象,并且能正确的调用service的api。service和客户端必须在同一个进程的原因是因为这种方式不能做跨进程的序列化。
比如:下面是一个可以让客户端通过Binder访问service方法的例子:
public class LocalService extends Service { // Binder given to clients private final IBinder mBinder = new LocalBinder(); // Random number generator private final Random mGenerator = new Random(); /** * Class used for the client Binder. Because we know this service always * runs in the same process as its clients, we don't need to deal with IPC. */ public class LocalBinder extends Binder { LocalService getService() { // Return this instance of LocalService so clients can call public methods return LocalService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } /** method for clients */ public int getRandomNumber() { return mGenerator.nextInt(100); } }LocalBinder给客户端提供了用来检索出当前LocalService实例的getService()方法。这就允许客户端调用service当中的public的方法。比如客户端可以调用service中的getRandomNumber()方法。
public class BindingActivity extends Activity { LocalService mService; boolean mBound = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to LocalService Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // Unbind from the service if (mBound) { unbindService(mConnection); mBound = false; } } /** Called when a button is clicked (the button in the layout file attaches to * this method with the android:onClick attribute) */ public void onButtonClick(View v) { if (mBound) { // Call a method from the LocalService. // However, if this call were something that might hang, then this request should // occur in a separate thread to avoid slowing down the activity performance. int num = mService.getRandomNumber(); Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); } } /** Defines callbacks for service binding, passed to bindService() */ private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { // We've bound to LocalService, cast the IBinder and get LocalService instance LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } }; }上面的例子展示了客户端如何使用ServiceConnection和onServiceConnected()回调绑定到service上。下面的章节提供了更多关于绑定到service的知识。
public class MessengerService extends Service { /** Command to the service to display a message */ static final int MSG_SAY_HELLO = 1; /** * Handler of incoming messages from clients. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ final Messenger mMessenger = new Messenger(new IncomingHandler()); /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ @Override public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); return mMessenger.getBinder(); } }注意,handler的handleMessage()方法也是service收到消息并根据消息的what成员决定如何处理消息的地方。
public class ActivityMessenger extends Activity { /** Messenger for communicating with the service. */ Messenger mService = null; /** Flag indicating whether we have called bind on the service. */ boolean mBound; /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the object we can use to // interact with the service. We are communicating with the // service using a Messenger, so here we get a client-side // representation of that from the raw IBinder object. mService = new Messenger(service); mBound = true; } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mService = null; mBound = false; } }; public void sayHello(View v) { if (!mBound) return; // Create and send a message to the service, using a supported 'what' value Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to the service bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // Unbind from the service if (mBound) { unbindService(mConnection); mBound = false; } } }
package com.example.android.apis.app; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.util.Log; import android.widget.Toast; import java.util.ArrayList; // Need the following import to get access to the app resources, since this // class is in a sub-package. import com.example.android.apis.R; import com.example.android.apis.app.RemoteService.Controller; /** * This is an example of implementing an application service that uses the * {@link Messenger} class for communicating with clients. This allows for * remote interaction with a service, without needing to define an AIDL * interface. * * <p>Notice the use of the {@link NotificationManager} when interesting things * happen in the service. This is generally how background services should * interact with the user, rather than doing something more disruptive such as * calling startActivity(). */ public class MessengerService extends Service { /** For showing and hiding our notification. */ NotificationManager mNM; /** Keeps track of all current registered clients. */ ArrayList<Messenger> mClients = new ArrayList<Messenger>(); /** Holds last value set by a client. */ int mValue = 0; /** * Command to the service to register a client, receiving callbacks * from the service. The Message's replyTo field must be a Messenger of * the client where callbacks should be sent. */ static final int MSG_REGISTER_CLIENT = 1; /** * Command to the service to unregister a client, ot stop receiving callbacks * from the service. The Message's replyTo field must be a Messenger of * the client as previously given with MSG_REGISTER_CLIENT. */ static final int MSG_UNREGISTER_CLIENT = 2; /** * Command to service to set a new value. This can be sent to the * service to supply a new value, and will be sent by the service to * any registered clients with the new value. */ static final int MSG_SET_VALUE = 3; /** * Handler of incoming messages from clients. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_REGISTER_CLIENT: mClients.add(msg.replyTo); break; case MSG_UNREGISTER_CLIENT: mClients.remove(msg.replyTo); break; case MSG_SET_VALUE: mValue = msg.arg1; for (int i=mClients.size()-1; i>=0; i--) { try { mClients.get(i).send(Message.obtain(null, MSG_SET_VALUE, mValue, 0)); } catch (RemoteException e) { // The client is dead. Remove it from the list; // we are going through the list from back to front // so this is safe to do inside the loop. mClients.remove(i); } } break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ final Messenger mMessenger = new Messenger(new IncomingHandler()); @Override public void onCreate() { mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); // Display a notification about us starting. showNotification(); } @Override public void onDestroy() { // Cancel the persistent notification. mNM.cancel(R.string.remote_service_started); // Tell the user we stopped. Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show(); } /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } /** * Show a notification while this service is running. */ private void showNotification() { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.remote_service_started); // Set the icon, scrolling text and timestamp Notification notification = new Notification(R.drawable.stat_sample, text, System.currentTimeMillis()); // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0); // Set the info for the views that show in the notification panel. notification.setLatestEventInfo(this, getText(R.string.remote_service_label), text, contentIntent); // Send the notification. // We use a string id because it is a unique number. We use it later to cancel. mNM.notify(R.string.remote_service_started, notification); } }
package com.example.android.apis.app; import com.example.android.apis.R; import com.example.android.apis.app.LocalServiceActivities.Binding; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class MessengerServiceActivities { /** * Example of binding and unbinding to the remote service. * This demonstrates the implementation of a service which the client will * bind to, interacting with it through an aidl interface.</p> * * <p>Note that this is implemented as an inner class only keep the sample * all together; typically this code would appear in some separate class. */ public static class Binding extends Activity { /** Messenger for communicating with service. */ Messenger mService = null; /** Flag indicating whether we have called bind on the service. */ boolean mIsBound; /** Some text view we are using to show state information. */ TextView mCallbackText; /** * Handler of incoming messages from service. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MessengerService.MSG_SET_VALUE: mCallbackText.setText("Received from service: " + msg.arg1); break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ final Messenger mMessenger = new Messenger(new IncomingHandler()); /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the service object we can use to // interact with the service. We are communicating with our // service through an IDL interface, so get a client-side // representation of that from the raw service object. mService = new Messenger(service); mCallbackText.setText("Attached."); // We want to monitor the service for as long as we are // connected to it. try { Message msg = Message.obtain(null, MessengerService.MSG_REGISTER_CLIENT); msg.replyTo = mMessenger; mService.send(msg); // Give it some value as an example. msg = Message.obtain(null, MessengerService.MSG_SET_VALUE, this.hashCode(), 0); mService.send(msg); } catch (RemoteException e) { // In this case the service has crashed before we could even // do anything with it; we can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. } // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_connected, Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mService = null; mCallbackText.setText("Disconnected."); // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_disconnected, Toast.LENGTH_SHORT).show(); } }; void doBindService() { // Establish a connection with the service. We use an explicit // class name because there is no reason to be able to let other // applications replace our component. bindService(new Intent(Binding.this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); mIsBound = true; mCallbackText.setText("Binding."); } void doUnbindService() { if (mIsBound) { // If we have received the service, and hence registered with // it, then now is the time to unregister. if (mService != null) { try { Message msg = Message.obtain(null, MessengerService.MSG_UNREGISTER_CLIENT); msg.replyTo = mMessenger; mService.send(msg); } catch (RemoteException e) { // There is nothing special we need to do if the service // has crashed. } } // Detach our existing connection. unbindService(mConnection); mIsBound = false; mCallbackText.setText("Unbinding."); } } /** * Standard initialization of this activity. Set up the UI, then wait * for the user to poke it before doing anything. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.messenger_service_binding); // Watch for button clicks. Button button = (Button)findViewById(R.id.bind); button.setOnClickListener(mBindListener); button = (Button)findViewById(R.id.unbind); button.setOnClickListener(mUnbindListener); mCallbackText = (TextView)findViewById(R.id.callback); mCallbackText.setText("Not attached."); } private OnClickListener mBindListener = new OnClickListener() { public void onClick(View v) { doBindService(); } }; private OnClickListener mUnbindListener = new OnClickListener() { public void onClick(View v) { doUnbindService(); } }; } }
//LocalService mService; private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established public void onServiceConnected(ComponentName className, IBinder service) { // Because we have bound to an explicit // service that is running in our own process, we can // cast its IBinder to a concrete class and directly access it. LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } // Called when the connection with the service disconnects unexpectedly public void onServiceDisconnected(ComponentName className) { Log.e(TAG, "onServiceDisconnected"); mBound = false; } };使用这个ServiceConnection,客户端可以把它传递到bindService()中来绑定到service上,就像:
Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE);(1)bindService()的第一个参数是一个intent,这个intent会明确的指明了要绑定的service的名字(当然intent也可以是隐式的)。
package com.example.android.apis.app; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.RemoteException; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Process; import android.os.RemoteCallbackList; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; // Need the following import to get access to the app resources, since this // class is in a sub-package. import com.example.android.apis.R; /** * This is an example of implementing an application service that runs in a * different process than the application. Because it can be in another * process, we must use IPC to interact with it. The * {@link Controller} and {@link Binding} classes * show how to interact with the service. * * <p>Note that most applications <strong>do not</strong> need to deal with * the complexity shown here. If your application simply has a service * running in its own process, the {@link LocalService} sample shows a much * simpler way to interact with it. */ public class RemoteService extends Service { /** * This is a list of callbacks that have been registered with the * service. Note that this is package scoped (instead of private) so * that it can be accessed more efficiently from inner classes. */ final RemoteCallbackList<IRemoteServiceCallback> mCallbacks = new RemoteCallbackList<IRemoteServiceCallback>(); int mValue = 0; NotificationManager mNM; @Override public void onCreate() { mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); // Display a notification about us starting. showNotification(); // While this service is running, it will continually increment a // number. Send the first message that is used to perform the // increment. mHandler.sendEmptyMessage(REPORT_MSG); } @Override public void onDestroy() { // Cancel the persistent notification. mNM.cancel(R.string.remote_service_started); // Tell the user we stopped. Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show(); // Unregister all callbacks. mCallbacks.kill(); // Remove the next pending message to increment the counter, stopping // the increment loop. mHandler.removeMessages(REPORT_MSG); } @Override public IBinder onBind(Intent intent) { // Select the interface to return. If your service only implements // a single interface, you can just return it here without checking // the Intent. if (IRemoteService.class.getName().equals(intent.getAction())) { return mBinder; } if (ISecondary.class.getName().equals(intent.getAction())) { return mSecondaryBinder; } return null; } /** * The IRemoteInterface is defined through IDL */ private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { public void registerCallback(IRemoteServiceCallback cb) { if (cb != null) mCallbacks.register(cb); } public void unregisterCallback(IRemoteServiceCallback cb) { if (cb != null) mCallbacks.unregister(cb); } }; /** * A secondary interface to the service. */ private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() { public int getPid() { return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { } }; @Override public void onTaskRemoved(Intent rootIntent) { Toast.makeText(this, "Task removed: " + rootIntent, Toast.LENGTH_LONG).show(); } private static final int REPORT_MSG = 1; /** * Our Handler used to execute operations on the main thread. This is used * to schedule increments of our value. */ private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { // It is time to bump the value! case REPORT_MSG: { // Up it goes. int value = ++mValue; // Broadcast to all clients the new value. final int N = mCallbacks.beginBroadcast(); for (int i=0; i<N; i++) { try { mCallbacks.getBroadcastItem(i).valueChanged(value); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing // the dead object for us. } } mCallbacks.finishBroadcast(); // Repeat every 1 second. sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000); } break; default: super.handleMessage(msg); } } }; /** * Show a notification while this service is running. */ private void showNotification() { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.remote_service_started); // Set the icon, scrolling text and timestamp Notification notification = new Notification(R.drawable.stat_sample, text, System.currentTimeMillis()); // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0); // Set the info for the views that show in the notification panel. notification.setLatestEventInfo(this, getText(R.string.remote_service_label), text, contentIntent); // Send the notification. // We use a string id because it is a unique number. We use it later to cancel. mNM.notify(R.string.remote_service_started, notification); } // ---------------------------------------------------------------------- /** * <p>Example of explicitly starting and stopping the remove service. * This demonstrates the implementation of a service that runs in a different * process than the rest of the application, which is explicitly started and stopped * as desired.</p> * * <p>Note that this is implemented as an inner class only keep the sample * all together; typically this code would appear in some separate class. */ public static class Controller extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.remote_service_controller); // Watch for button clicks. Button button = (Button)findViewById(R.id.start); button.setOnClickListener(mStartListener); button = (Button)findViewById(R.id.stop); button.setOnClickListener(mStopListener); } private OnClickListener mStartListener = new OnClickListener() { public void onClick(View v) { // Make sure the service is started. It will continue running // until someone calls stopService(). // We use an action code here, instead of explictly supplying // the component name, so that other packages can replace // the service. startService(new Intent( "com.example.android.apis.app.REMOTE_SERVICE")); } }; private OnClickListener mStopListener = new OnClickListener() { public void onClick(View v) { // Cancel a previous call to startService(). Note that the // service will not actually stop at this point if there are // still bound clients. stopService(new Intent( "com.example.android.apis.app.REMOTE_SERVICE")); } }; } // ---------------------------------------------------------------------- /** * Example of binding and unbinding to the remote service. * This demonstrates the implementation of a service which the client will * bind to, interacting with it through an aidl interface.</p> * * <p>Note that this is implemented as an inner class only keep the sample * all together; typically this code would appear in some separate class. */ public static class Binding extends Activity { /** The primary interface we will be calling on the service. */ IRemoteService mService = null; /** Another interface we use on the service. */ ISecondary mSecondaryService = null; Button mKillButton; TextView mCallbackText; private boolean mIsBound; /** * Standard initialization of this activity. Set up the UI, then wait * for the user to poke it before doing anything. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.remote_service_binding); // Watch for button clicks. Button button = (Button)findViewById(R.id.bind); button.setOnClickListener(mBindListener); button = (Button)findViewById(R.id.unbind); button.setOnClickListener(mUnbindListener); mKillButton = (Button)findViewById(R.id.kill); mKillButton.setOnClickListener(mKillListener); mKillButton.setEnabled(false); mCallbackText = (TextView)findViewById(R.id.callback); mCallbackText.setText("Not attached."); } /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the service object we can use to // interact with the service. We are communicating with our // service through an IDL interface, so get a client-side // representation of that from the raw service object. mService = IRemoteService.Stub.asInterface(service); mKillButton.setEnabled(true); mCallbackText.setText("Attached."); // We want to monitor the service for as long as we are // connected to it. try { mService.registerCallback(mCallback); } catch (RemoteException e) { // In this case the service has crashed before we could even // do anything with it; we can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. } // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_connected, Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mService = null; mKillButton.setEnabled(false); mCallbackText.setText("Disconnected."); // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_disconnected, Toast.LENGTH_SHORT).show(); } }; /** * Class for interacting with the secondary interface of the service. */ private ServiceConnection mSecondaryConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // Connecting to a secondary interface is the same as any // other interface. mSecondaryService = ISecondary.Stub.asInterface(service); mKillButton.setEnabled(true); } public void onServiceDisconnected(ComponentName className) { mSecondaryService = null; mKillButton.setEnabled(false); } }; private OnClickListener mBindListener = new OnClickListener() { public void onClick(View v) { // Establish a couple connections with the service, binding // by interface names. This allows other applications to be // installed that replace the remote service by implementing // the same interface. bindService(new Intent(IRemoteService.class.getName()), mConnection, Context.BIND_AUTO_CREATE); bindService(new Intent(ISecondary.class.getName()), mSecondaryConnection, Context.BIND_AUTO_CREATE); mIsBound = true; mCallbackText.setText("Binding."); } }; private OnClickListener mUnbindListener = new OnClickListener() { public void onClick(View v) { if (mIsBound) { // If we have received the service, and hence registered with // it, then now is the time to unregister. if (mService != null) { try { mService.unregisterCallback(mCallback); } catch (RemoteException e) { // There is nothing special we need to do if the service // has crashed. } } // Detach our existing connection. unbindService(mConnection); unbindService(mSecondaryConnection); mKillButton.setEnabled(false); mIsBound = false; mCallbackText.setText("Unbinding."); } } }; private OnClickListener mKillListener = new OnClickListener() { public void onClick(View v) { // To kill the process hosting our service, we need to know its // PID. Conveniently our service has a call that will return // to us that information. if (mSecondaryService != null) { try { int pid = mSecondaryService.getPid(); // Note that, though this API allows us to request to // kill any process based on its PID, the kernel will // still impose standard restrictions on which PIDs you // are actually able to kill. Typically this means only // the process running your application and any additional // processes created by that app as shown here; packages // sharing a common UID will also be able to kill each // other's processes. Process.killProcess(pid); mCallbackText.setText("Killed service process."); } catch (RemoteException ex) { // Recover gracefully from the process hosting the // server dying. // Just for purposes of the sample, put up a notification. Toast.makeText(Binding.this, R.string.remote_call_failed, Toast.LENGTH_SHORT).show(); } } } }; // ---------------------------------------------------------------------- // Code showing how to deal with callbacks. // ---------------------------------------------------------------------- /** * This implementation is used to receive callbacks from the remote * service. */ private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() { /** * This is called by the remote service regularly to tell us about * new values. Note that IPC calls are dispatched through a thread * pool running in each process, so the code executing here will * NOT be running in our main thread like most other things -- so, * to update the UI, we need to use a Handler to hop over there. */ public void valueChanged(int value) { mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0)); } }; private static final int BUMP_MSG = 1; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case BUMP_MSG: mCallbackText.setText("Received from service: " + msg.arg1); break; default: super.handleMessage(msg); } } }; } // ---------------------------------------------------------------------- /** * Examples of behavior of different bind flags.</p> */ public static class BindingOptions extends Activity { ServiceConnection mCurConnection; TextView mCallbackText; class MyServiceConnection implements ServiceConnection { final boolean mUnbindOnDisconnect; public MyServiceConnection() { mUnbindOnDisconnect = false; } public MyServiceConnection(boolean unbindOnDisconnect) { mUnbindOnDisconnect = unbindOnDisconnect; } public void onServiceConnected(ComponentName className, IBinder service) { if (mCurConnection != this) { return; } mCallbackText.setText("Attached."); Toast.makeText(BindingOptions.this, R.string.remote_service_connected, Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { if (mCurConnection != this) { return; } mCallbackText.setText("Disconnected."); Toast.makeText(BindingOptions.this, R.string.remote_service_disconnected, Toast.LENGTH_SHORT).show(); if (mUnbindOnDisconnect) { unbindService(this); mCurConnection = null; Toast.makeText(BindingOptions.this, R.string.remote_service_unbind_disconn, Toast.LENGTH_SHORT).show(); } } } /** * Standard initialization of this activity. Set up the UI, then wait * for the user to poke it before doing anything. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.remote_binding_options); // Watch for button clicks. Button button = (Button)findViewById(R.id.bind_normal); button.setOnClickListener(mBindNormalListener); button = (Button)findViewById(R.id.bind_not_foreground); button.setOnClickListener(mBindNotForegroundListener); button = (Button)findViewById(R.id.bind_above_client); button.setOnClickListener(mBindAboveClientListener); button = (Button)findViewById(R.id.bind_allow_oom); button.setOnClickListener(mBindAllowOomListener); button = (Button)findViewById(R.id.bind_waive_priority); button.setOnClickListener(mBindWaivePriorityListener); button = (Button)findViewById(R.id.bind_important); button.setOnClickListener(mBindImportantListener); button = (Button)findViewById(R.id.bind_with_activity); button.setOnClickListener(mBindWithActivityListener); button = (Button)findViewById(R.id.unbind); button.setOnClickListener(mUnbindListener); mCallbackText = (TextView)findViewById(R.id.callback); mCallbackText.setText("Not attached."); } private OnClickListener mBindNormalListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE)) { mCurConnection = conn; } } }; private OnClickListener mBindNotForegroundListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND)) { mCurConnection = conn; } } }; private OnClickListener mBindAboveClientListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_ABOVE_CLIENT)) { mCurConnection = conn; } } }; private OnClickListener mBindAllowOomListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT)) { mCurConnection = conn; } } }; private OnClickListener mBindWaivePriorityListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(true); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY)) { mCurConnection = conn; } } }; private OnClickListener mBindImportantListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT)) { mCurConnection = conn; } } }; private OnClickListener mBindWithActivityListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_ADJUST_WITH_ACTIVITY | Context.BIND_WAIVE_PRIORITY)) { mCurConnection = conn; } } }; private OnClickListener mUnbindListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } } }; } }
此外,如果你的service是started并且也接受绑定,那么当系统调用onUnbind()的时候,如果你希望下次有客户端绑定到service的时候你可以收到onRebind()调用的话(而不是收到onbind()回调),你可以在onUnbind()中返回true。onRebind()返回空,但是客户端仍然可以在onServiceConnected()中收到IBinder。下面的图1展示了这种类型的service的生命周期。