The AIDL (Android Interface Definition Language) IPC Mechanism is object-oriented and lightweight. The interface defined by AIDL can implement IPC communication between the server and the client. On Android, a process cannot access the memory of other processes just as it accesses the memory of the current process. Therefore, to talk between processes, You need to split the objects into basic data units that the operating system can understand and orderly pass through the process boundary. It is tedious to implement this data transmission process through code. Fortunately, android provides the AIDL tool to help us complete this task.
Note: you only need the client of application A to access the server of application B to implement IPC communication, and use AIDL when the server needs to process multi-thread (client) access. If you do not need to use IPC communication between processes, it is more appropriate to use the Binder interface. If you need to implement IPC communication between processes, but do not need to process multithreading (multiple clients ), it is more appropriate to use the Messager interface. However, before using AIDL, make sure that you understand the Bound Service.
The AIDL interface uses a direct function call method, but you cannot predict which process (or thread) will call this interface. There are differences between thread calls of the same process and other processes that call this interface:
- When the AIDL interface is called in the same process, the execution of the AIDL interface code will be completed in the thread that calls the AIDL interface. If the AIDL interface is called in the main UI thread, then the AIDL interface code will be executed in this main UI thread. For other threads, The AIDL interface code is executed in the service. Therefore, if only the threads in this process access this service, you can completely control which threads will access this service (but if so, there is no need to use AIDL at all, the Binder interface is more suitable ).
- When a remote process (other threads) calls the AIDL interface, a thread is assigned to the thread pool of the AIDL process to execute the AIDL code. Therefore, when AIDL is compiled, you must be prepared that there may be unknown thread access and multiple calls may occur at the same time (multiple thread access). Therefore, the implementation of the ADIL interface must be thread-safe.
- The oneway keyword can be used to indicate the behavior attribute of a remote call. If this keyword is used, the remote call only transmits the data required for the call and returns immediately without waiting for the result to be returned, that is to say, it does not block the running of remote threads. The AIDL interface will eventually obtain a call from the Binder thread pool (similar to a Common Remote Call ). If the keyword oneway is used in a local call, the function call will not be affected.
Define the AIDL Interface
The AIDL interface uses the suffix. aidl file to define ,. the aidl file is written in java syntax. the aidl file is saved in the src/directory (the same copy is required for both the server and client, that is to say, as long as the application that needs to use the AIDL interface has a copy under its src directory. aidl file copy ).
During compilation, the Android sdk generates an IBinder interface for the. aidl file under the src/directory under the gen/directory. The server must implement the IBinder interface accordingly. The client can bind the service and call the methods to implement IPC communication.
To create a server implemented by AIDL, perform the following steps:
1. Create a. aidl file:
This file (YourInterface. aidl) defines available client methods and data interfaces
2. implement this interface:
The Android SDK will generate an aidl interface based on your. AIDL file. The generated interface contains an abstract internal class named Stub, which declares all. the methods described in aidl must inherit and implement the Stub class in the code. the method defined in aidl.
3. Publish the server interface to the client:
Implement a Service and return the sub-class (Implementation class) of the Stub class implemented in step 1 in the onBinder method ).
Note:
Any modifications to AIDL on the server must be synchronized to all clients, otherwise, the client may call the interface obtained by the server, causing a program exception (because the client may call the interface no longer supported by the server at this time ).
1. Create a. aidl File
AIDL uses a simple syntax to declare an interface and describe the parameters and return values of its methods and methods. These parameters and return values can be of any type, or even interfaces generated by other AIDL. It is important to import all non-built-in types, even if these types are in the same package as the interface.
The default AIDL supports the following data types (these types do not need to be imported through import ):
- Original data types of java (including int, long, char, boolen, etc)
- String
- CharSequence: this class is the character sequence used by TextView and other control objects.
- List: all elements in the List must be of the type listed here, including the APIs and packable types generated by other AIDL. List can be used as a general class (such as List <String>). The specific class received by the other side is generally an ArrayList. These methods use the List interface.
- Map: all elements in the Map must be the types listed here, including the interfaces and packable types generated by other AIDL. General maps (such as Map <String, Integer>) are not supported. The specific class received by the other side is generally a HashMap. These methods use the Map interface.
For other types, import must be used in aidl, even if the type and aidl are in the same package.
When defining a server interface, pay attention to the following points:
- The method can have 0 or more parameters, so that the NULL return value can also return the required data.
- All parameters of the non-original data type must specify the parameter direction (whether to pass the parameter or the outgoing parameter). The incoming parameter is marked with the in keyword, the outgoing parameter is used for the outgoing parameter, and the incoming parameter is used for the outgoing parameter. If no display is specified, in is used by default.
- All comments in the aidl file will be included in the generated IBinder interface (except for comments on the Import and pacakge statements ).
- Aidl only supports Member methods, but does not support member variables.
Tip:
It is necessary to limit the transmission direction of parameters because grouping (serialization) parameters are expensive.
The following is an example of a. aidl file.
// IRemoteService.aidlpackage com.example.android;// Declare any non-default types here with import statements/** Example service interface */interface IRemoteService { /** Request the process ID of this service, to do evil things with it. */ int getPid(); /** Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);}
Save the. aidl file to the src/directory in the project directory. When the apk is compiled and generated, the sdk tool will generate a. java file for the corresponding IBiner interface under the gen/directory.
If you use eclipse to write an app, The IBinder interface file will be generated instantly. If you do not use eclipse, the Ant tool will generate this IBinder interface file when you compile your app next time. use ant debug to compile your app immediately after the aidl file, so that your subsequent code can use the generated IBinder interface class.
2. Implementation Interface
The generated interface contains an abstract internal class named Stub, which declares all the methods described in. aidl,
Note:
Stub also defines a small number of auxiliary methods, especially asInterface (), through which or to obtain IBinder (when applicationContext. when bindService () is successfully called, it is passed to the onServiceConnected () of the client and an interface instance used to call the IPC method is returned. For more details, seeCalling an IPC Method.
To implement your own interface, start with YourInterface. stub class inheritance, and then implement the relevant methods (you can create. the aidl file then implements the stub method instead of intermediate compilation. java file processing. aidl file ).
The following example calls the IRemoteService interface. An anonymous object is used here.
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { public int getPid(){ return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { // Does nothing }};
In this way, mBinder is a Stub class object, which provides the RPC interface for the service. In the next step, this object will be made public to the client, so that the client can interact with the service through this object.
Note the following points when implementing the ADIL interface:
- It is not guaranteed that all calls to the aidl interface are executed in the main thread. Therefore, multi-threaded calls must be considered, that is, thread security must be considered.
- By default, IPC calls are synchronized. If it is known that the IPC server will take many milliseconds to complete, do not call it in the Activity or View thread, otherwise, the application will be suspended (Android may display the "Application not responding" dialog box). You can try to call it in an independent thread.
- The exception is not returned to the caller.
3. Expose the interface to the client
After implementing the interface, you need to expose the interface to the client, that is, publish the Service. The implementation method is to inherit the Service and then implement the Service. onBind (Intent) returns a class object that implements the interface. The following code exposes the IRemoteService interface to the client.
public class RemoteService extends Service { @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { // Return the interface return mBinder; } private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { public int getPid(){ return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { // Does nothing } };}
Now, if the client (such as an Activity) calls bindService () to connect to this server (RemoteService), The onServiceConnected () callback function of the client will obtain the onBind () from the server (RemoteService () the returned mBinder object.
The client has to access this interface class (IRemoteService), so if the server and client are not in the same process (application, the client must have the same copy as the server under the src/directory. aidl file copy (also, the package name, class name, and content are exactly the same), the client will use this. aidl file generationAndroid. OS. Binder interface -- to implement the client access method in AIDL.
After the client obtains the IBinder object in the onServiceConnected () callback method, it must callYourServiceInterface. Stub. asInterface (service) converts itYourServiceInterfaceType. For example:
IRemoteService mIRemoteService;private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established public void onServiceConnected(ComponentName className, IBinder service) { // Following the example above for an AIDL interface, // this gets an instance of the IRemoteInterface, which we can use to call on the service mIRemoteService = IRemoteService.Stub.asInterface(service); } // Called when the connection with the service disconnects unexpectedly public void onServiceDisconnected(ComponentName className) { Log.e(TAG, "Service has unexpectedly disconnected"); mIRemoteService = null; }};
For more sample code, see the ApiDemos exampleRemoteService.java
.
4. Passing objects through IPC
If you want to pass a class from one process to another through the IPC interface, this can be done, but you must ensure the validity of this class at both ends of the IPC, and this class must supportParcelable interface. Support for ParcelableThe interface is very important, because this allows the Android system to split the Object into the original data type, in order to achieve cross-process sending (serialized sending ).
To create a class that supports the Parcelable protocol, perform the following steps:
1. Enable this class to implement the Parcelabel interface.
2. Implement the public void writeToParcel (Parcel out) method to write the current state of the object to the wrapped object (Parcel ).
3. Add a static object of the Parcelable. Creator interface to the class.CREATOR
.
4. finally, create an AIDL file to declare this packable class (see the Rect below. aidl). If you are using a custom compilation process, do not compile this AIDL file. It does not need to be compiled like a C header file.
AIDL uses the member serialization and deserialization objects of these methods. The following code demonstrates how to enable the Rect class to support serialization (parcelable)
package android.graphics;// Declare Rect so AIDL can find it and knows that it implements// the parcelable protocol.parcelable Rect;
The following example demonstrates how to implement the Parcelable protocol for the Rect class.
import android.os.Parcel;import android.os.Parcelable;public final class Rect implements Parcelable { public int left; public int top; public int right; public int bottom; public static final Parcelable.Creator<Rect> CREATOR = newParcelable.Creator<Rect>() { public Rect createFromParcel(Parcel in) { return new Rect(in); } public Rect[] newArray(int size) { return new Rect[size]; } }; public Rect() { } private Rect(Parcel in) { readFromParcel(in); } public void writeToParcel(Parcel out) { out.writeInt(left); out.writeInt(top); out.writeInt(right); out.writeInt(bottom); } public void readFromParcel(Parcel in) { left = in.readInt(); top = in.readInt(); right = in.readInt(); bottom = in.readInt(); }}
The Rect class serialization is quite simple here. For other types of data that can be packaged, seeParcel
Class.
Warning do not forget to perform security checks on the data received from other processes. In the above example, the rect reads four values from the data packet and needs to confirm that these values are within the acceptable range regardless of what the caller wants to do. For more information about maintaining application security, seeSecurity and Permissions.
5. Call the ICP method
The following describes how to call the remote AIDL interface:
1. Include the. adil file in the src/directory.
2. DeclareIBinder
Interface (generated through the. aidl file) instance.
3. ImplementationServiceConnection
.
4. CallContext. bindService () bind your ServiceConnection implementation Class Object (that is, remote server ).
5. InonServiceConnected()
Method will receive the IBinder object (that is, the server), callYourInterfaceName.Stub.asInterface((IBinder)service)
Convert the return valueYourInterfaceType.
6. Call the methods defined in the interface and always catch the DeadObjectException exception thrown when the connection is interrupted. This is the only exception that may be thrown by the remote method.
7. CallThe Context. unbindService () method is disconnected.
Here are a few tips for calling the IPC service:
- Objects are referenced and counted between processes.
- Anonymous objects can be sent as method parameters.
For more information about service binding, see the relevant documentation of Bound Services.
The following is the Demo code of the AIDL-created Service, which is extracted from the Remote Service in the ApiDemos project.
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); } } };}
Note: Due to the limited level of translators, if it is found that there is something wrong, I hope to point out that I am not very grateful and make progress together!
Address: http://developer.android.com/guide/developing/tools/aidl.html