AndroidService in-depth analysis (2)
AndroidService in-depth analysis (2)
In the previous article, we tested and summarized the Service lifecycle. This article describes how to bind a running Service.
The Service bound to the running instance may be a Service provided only for this application, which is called a local Service. It may also provide cross-process services for other applications, that is, remote services. The following sections describe:
Local Service
If the Service only serves this application, we only need to inherit the Binder class and define the method we need to implement. When binding a connection is initiated, the Service will return the object of this inheritance class in the onBind method, so that the client and Service share a Binder object. The Binder is like a bridge that enables clients and services to communicate with each other. The following is an example of local Service implementation:
LocalService code:
Public classLocalService extends Service {private String TAG = getClass (). getSimpleName (); MyBinder myBinder = new MyBinder (); ServiceListener myServiceListener; public LocalService () {} public interface ServiceListener {public String getActivityInfo ();} private void setListener (listener) {this. myServiceListener = myServiceListener;} // After the binding is successful, the Service can use this method to obtain the message of the Activity. Private void getActivityInfo () {String activityInfo = myServiceListener. getActivityInfo (); Log. d (TAG, TAG ++ activityInfo ------> + activityInfo);} private String getInfo () {return Hello, I am a LocalService method, you can access me through its object !;} Public class MyBinder extends Binder {public String getServiceInfo () {return getInfo ();} public void setServiceListener (ServiceListenermyServiceListener) {setListener (myServiceListener );}} @ Override public IBinder onBind (Intent intent) {Log. d (TAG, TAG + ------> onBind (); return myBinder ;}@ Override public void onRebind (Intent intent) {Log. d (TAG, TAG + ------> onRebind (); super. onRebind (intent) ;}@ Override public boolean onUnbind (Intent intent) {Log. d (TAG, TAG + ------> onUnbind (); // return false; the returned value determines whether to execute onRebind return true during next binding ;}}
LocalActivity code:
Public classLocalActivity extends ActionBarActivity {private String TAG = getClass (). getSimpleName (); Intent serviceIntent; @ Override protected void onCreate (BundlesavedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_local); Log. d (TAG, TAG + ------> onCreate (); serviceIntent = new Intent (this, LocalService. class);} public void bindService (View v) {Log. d (TAG, TA G + ------> bindService (); bindService (serviceIntent, serviceConnection, Service. BIND_AUTO_CREATE);} public void unBindService (View v) {Log. d (TAG, TAG + ------> unBindService (); unbindService (serviceConnection) ;}@ Override protected void onDestroy () {Log. d (TAG, TAG + ------> onDestroy (); super. onDestroy ();} ServiceConnection serviceConnection = newServiceConnection () {@ Override public voidonServ IceConnected (ComponentName name, IBinder service) {Log. d (TAG, TAG + ------> onServiceConnected (); LocalService. myBinder binder = (LocalService. myBinder) service; String localServiceInfo = binder. getServiceInfo (); Log. d (TAG, TAG ++ onServiceConnected ------> + localServiceInfo); binder. setServiceListener (newLocalService. serviceListener () {@ Override public String getActivityInfo () {return Hello, in the Local In Activity, LocalService can call me to get the message of LocalActivity !; }) ;}@ Override public voidonServiceDisconnected (ComponentName name) {Log. d (TAG, TAG + ------> onServiceDisconnected ());}};}
The corresponding layout of the Activity is two buttons, respectively, to bind and unbind functions,
Both Activity and Service must be registered in the Manifest file.
Start the Activity and bind the Service successively. The output log is as follows:
03-17 10:10:58. 525 D/LocalActivity: LocalActivity ------> onCreate () 03-17 10:11:00. 955 D/LocalActivity: LocalActivity ------> bindService () 03-17 10:11:00. 975 D/LocalService: LocalService ------> onBind () 03-17 10:11:00. 995 D/LocalActivity: LocalActivity ------> onServiceConnected () 03-17 10:11:00. 995 D/LocalActivity: LocalActivity + onServiceConnected ------> Hello, I am a LocalService method. You can access me through its object! 03-17 10:11:16. 345 D/LocalActivity: LocalActivity ------> unBindService () 03-17 10:11:16. 345 D/LocalService: LocalService ------> onUnbind ()
The above log shows that we have indeed achieved Service binding and unbinding. In addition, you should also find that we have implemented mutual calls between services and activities. Yes. In actual work, we need not only to instruct the Service to provide services for us, but also to obtain client data to provide services better (the getActivityInfo method in LocalService is implemented through callback ).
Here I will summarize the specific implementation process:
1. In the Service class, design the internal class MyBinder that inherits the Binder class and add the method to be provided to the Activity. In this example, the getServiceInfo method implements the function of obtaining Service information. Sometimes, for convenience, we directly provide a method to return the Service object, but it is generally not recommended to do so. At the same time, we noticed the setServiceListener method, it is an important part of implementing the Service to call the Activity to provide methods. We use the callback method to implement Service access to the Activity;
2. Override the onBind method and return the MyBinder object. Now, the Service class is designed;
3. In the Activity, override the onServiceConnected and onServiceDisConnected methods of the ServiceConnection interface. In the onServiceConnected method, we obtain the MyBinder object returned by the onBinder method, then call the setServiceListener method to set the interface object required for Service access to Activity;
4. Now, the "bridge" between the Service and Activity has been built. In Service, we can use the getActivityInfo method to obtain Activity information. In Activity, we can also use the getServiceInfo method to obtain Service information.
Remote Service
When our Service needs to provide services for other applications, we need to use remote Service. The remote Service can be implemented in two ways: the Messenger mode and the AIDL (Andriod Interface Description Language) mode. The following is an introduction.
Messenger Mode
In this way, we define a Handler to process different Message objects. This Handler is the basis for Messenger to share IBinder with the client. It allows the client to send commands to the Service through the Message object. In addition, the client can also define a Messenger, so that the Service can also send messages to the client. This is the simplest way to implement inter-process communication, because the Messenger queue will be executed in a single thread, so we do not need to consider thread security.
To use Messenger for inter-process communication:
L implement a Handler to process the passed Message object;
L create a Messenger object and use the Handler object as the construction parameter;
L create an IBinder object using the Messenger object and return it through onBind;
L The client uses the received IBinder object as the Messenger constructor parameter to instantiate a Messenger object, which will have Handler references;
L the client sends a Message object through Handler, and the Service can process the Message through handleMessage of Handler.
The following is an example:
Create a MessengerService class in the ServiceTest project:
Public class MessengerService extendsService {/*** Command to the service to display a message */static final int MSG_SAY_HELLO = 1; static final int MSG_GET_CLIENT_MESSENGER = 2; static final int MSG_FROM_SERVICE = 3; private String TAG = getClass (). getSimpleName (); Messenger messengerToClient;/*** Handler of incoming messages from clients. */class ServiceIncomingHandler extends Handler {@ O Verride public void handleMessage (Message msg) {Log. d (TAG, TAG + ------> handleMessage (); switch (msg. what) {case MSG_SAY_HELLO: Log. d (TAG, handleMessage ------> MSG_SAY_HELLO !); Toast. makeText (getApplicationContext (), hello !, Toast. LENGTH_SHORT). show (); break; case MSG_GET_CLIENT_MESSENGER: Log. d (TAG, handleMessage ------> Service receives the Activity's messenger object !); // Obtain the Messenger object messengerToClient = msg that can send messages to the client. replyTo; Message serviceMsg = Message. obtain (null, MSG_FROM_SERVICE, 0, 0); try {// send the message messengerToClient to the client. send (serviceMsg);} catch (RemoteException e) {e. printStackTrace ();} break; default: super. handleMessage (msg) ;}}/ *** sends this serviceMessenger to the client, the client can use it to contact the Service */final Messenger serviceMessenger = new Messenger (newServiceIncomingHandler ();/*** When binding to the service, we return an interface to our messenger * for sending messages to the service. * // @ Override public IBinder onBind (Intent intent) {Log. d (TAG, TAG + ------> onBind (); Toast. makeText (getApplicationContext (), binding, Toast. LENGTH_SHORT ). show (); return serviceMessenger. getBinder ();}}
Create a MessengerTest project and create the ActivityMessenger class:
Public classActivityMessenger extends Activity {/*** Messenger for communicating with theservice. */Messenger messengerToService = null;/*** Flag indicating whether we have calledbind on the service. */boolean mBound; private String TAG = getClass (). getSimpleName ();/*** Command to the service to display amessage */static final int MSG_SAY_HELLO = 1; static final intMSG_SEND_MESSENGER_TO_SER VICE = 2; static final int MSG_FROM_SERVICE = 3;/*** Class for interacting with the maininterface of the service. */private ServiceConnection mConnection = newServiceConnection () {public voidonServiceConnected (ComponentName className, IBinder service) {// This is called when theconnection with the service has been // established, giving us theobject we can use to // interact with the service. We are communicating with the // service using a Messenger, sohere we get a client-side // representation of that from theraw IBinder object. log. d (TAG, TAG + ------> onServiceConnected (); messengerToService = newMessenger (service); mBound = true; Message msg = Message. obtain (null, MSG_SEND_MESSENGER_TO_SERVICE, 0, 0); msg. replyTo = activityMessenger; try {// send a message to the Messenger object. The msg object will be handed over to the hand object in the Service class. LeMessage processes messengerToService. send (msg);} catch (RemoteException e) {e. printStackTrace () ;}} public voidonServiceDisconnected (ComponentName className) {// This is called when theconnection with the service has been // unexpectedly disconnected -- that is, its process crashed. log. d (TAG, TAG + ------> onServiceDisconnected (); messengerToService = null; mBound = false ;}};/*** Handler of incom Ing messages fromservice. */class IncomingHandler extends Handler {@ Override public void handleMessage (Message msg) {Log. d (TAG, TAG + ------> handleMessage (); switch (msg. what) {case MSG_FROM_SERVICE: Log. d (TAG, TAG ++ MSG_FROM_SERVICE ------> the Activity receives a message from the Service !); Toast. makeText (getApplicationContext (), MSG_FROM_SERVICE !, Toast. LENGTH_SHORT ). show (); break; default: super. handleMessage (msg) ;}}/ *** Target we publish for service to sendmessages to IncomingHandler. */final Messenger activityMessenger = newMessenger (new IncomingHandler (); public void sayHello (View v) {Log. d (TAG, TAG + ------> sayHello (); if (! MBound) return; // Create and send a message to theservice, using a supported 'what 'value Message msg = Message. obtain (null, MSG_SAY_HELLO, 0, 0); try {// send a message to the Messenger object. The msg object will be handed to the handleMessage in the Service class to process messengerToService. send (msg);} catch (RemoteException e) {e. printStackTrace () ;}@ Override protected void onCreate (BundlesavedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); Log. d (TAG, TAG + ------> onCreate () ;}@ Override protected void onStart () {Log. d (TAG, TAG + ------> onStart (); super. onStart (); // Bind to the service bindService (newIntent (com. example. servicestudy. remoteservie. messengerService), mConnection, Context. BIND_AUTO_CREATE) ;}@ Override protected void onStop () {Log. d (TAG, TAG + ------> onStop (); super. onStop (); // Unbind from the service if (mBound) {unbindService (mConnection); mBound = false ;}}}
Both Acitivty and Service must be registered in the Manifest file. Service needs to set the android: exported attribute to true and intent-filter. As follows:
Okay, we have finished coding. Let's test it and print the log as follows:
03-1713: 11: 46.195 D/ActivityMessenger: ActivityMessenger ------> onCreate () 03-1713: 11: 46.195 D/ActivityMessenger: ActivityMessenger ------> onStart () 03-1713: 11: 46.215 D/MessengerService: MessengerService ------> onBind () 03-17 13:11:46. 235 D/ActivityMessenger: ActivityMessenger ------> onServiceConnected () 03-1713: 11: 46.275 D/MessengerService: MessengerService ------> handleMessage () 03-17 13:11:46. 275 D/MessengerSe Rvice: handleMessage ------> Service receives the Activity's messenger object! 03-17 13:11:46. 285 D/ActivityMessenger: ActivityMessenger ------> handleMessage () 03-17 13:11:46. 285 D/ActivityMessenger: ActivityMessenger + MSG_FROM_SERVICE ------> Activity receives a message from the Service! 03-1713: 11: 55.425 D/ActivityMessenger: ActivityMessenger ------> sayHello () 03-1713: 11: 55.425 D/MessengerService: MessengerService ------> handleMessage () 03-1713: 11: 55.425 D/MessengerService: handleMessage ------> MSG_SAY_HELLO! 03-1713: 12: 00.665 D/ActivityMessenger: ActivityMessenger ------> onStop ()
Have you seen it? The Activity and Service send messages to each other perfectly. They are all based on the Messenger object. The Activity obtains a Messenger object from Ibinder that can send messages to the Service, and then the Activity sends a message to the Service, and carry the Messenger object that can send messages to the Activity to achieve interaction between them. The logic is simple and easy to understand. Click here.
Now that Messenger is so easy to use, why should we continue to look at the AIDL method? I wonder if you have ever wondered whether the Messenger method queues all messages sent by the client and processes them in sequence, that is, the single-thread processing method, the advantage of this processing method is that it is easy to cause other problems, such as thread security. However, for services with high real-time requirements, this method may not be enough, maybe we need to adopt a multi-threaded method to process the received requests as soon as possible. At this time, we can directly use the AIDL method.
AIDL Method
In fact, I quietly tell you that the underlying implementation of the Messenger method is also implemented based on the AIDL method. To facilitate cross-process services, the system provides us with a Messenger class for convenient implementation, however, it may not meet our needs. At this time, we need to implement it directly based on AIDL.
In fact, the implementation of AIDL is not difficult, but there are a lot of details to note. I recommend an article here too, with code and summary: http://blog.csdn.net/songjinshi/article/details/22918405.
I excerpted the summary:
AIDL creation method:
The AIDL syntax is very simple. It can be used to declare an interface with one or more methods, or to pass parameters and return values. Due to the need for remote calls, these parameters and return values are not of any type. Below are some data types supported by AIDL:
1. Simple Java programming language types (int, boolean, etc.) that do not need to be declared by import)
2. Special declarations are not required for String and CharSequence.
3. list, Map, and Parcelables types. The data members of these types can only be simple data types, and Other types supported by the String type. (in addition, I did not try Parcelables. It cannot be compiled in Eclipse + ADT, and may be supported in the future ).
The AIDL syntax is as follows:
// File name: SomeClass. the aidl // file can contain comments, which are the same as those in java. // comments before the package will be ignored. // comments before functions and variables are added to the production java code. package com. cmcc. demo;
// Import the import Statement import com. cmcc. demo. ITaskCallback;
InterfaceITaskBinder {
// Functions, like java, can have 0 to multiple parameters, and can return a boolean isTaskRunning ();
VoidstopRunningTask (); // The parameter can be another aidl-defined interface void registerCallback (ITaskCallback cb );
VoidunregisterCallback (ITaskCallback cb );
// The parameter can be a String and can be input in the in table. The out parameter indicates the output type.
IntgetCustomerList (in String branch, out String customerList );
}
There are several principles for implementing interfaces:
. Do not return the thrown exception to the caller. It is not advisable to throw an exception across processes.
. IPC call is synchronous. If you know that an IPC service takes several milliseconds to complete, you should avoid calling it in the main thread of the Activity. That is, the IPC call will suspend the application and cause the interface to lose the response. In this case, you should consider a single thread for processing.
. Static attributes cannot be declared in the AIDL interface.
IPC call steps:
1. Declare an interface type variable, which is defined in the. aidl file.
2. Implement ServiceConnection.
3. Call ApplicationContext. bindService () and transfer it in ServiceConnection implementation.
4. in ServiceConnection. in onServiceConnected () implementation, you will receive an IBinder instance (called Service ). call YourInterfaceName. stub. asInterface (IBinder) service) converts parameters to YourInterface type.
5. Call the methods defined in the interface. You always need to detect the DeadObjectException exception, which is thrown when the connection is disconnected. It will only be thrown by remote methods.
6. Disconnect and call ApplicationContext. unbindService () in the interface instance ()
So far, all learning about services has been completed. We have completed a complete test of the Android Service, including the life cycle test, local binding to run the Service implementation, remote binding to run the Service in the Messenger mode and AIDL mode. All bound service examples enable the interaction between the Service and the client, that is, the Service can call the client method, or the client can call the Service method.