Android uses AIDL for cross-process communication (IPC)

Source: Internet
Author: User

Android uses AIDL for cross-process communication (IPC)

Before you do anything, do not think it is very difficult, because you have been afraid of it before you begin, this will prevent your progress. Maybe it is not very difficult when you start this step.

I. AIDL Overview

Meaning: AIDL (Android Interface Definition Language) is the android Interface Definition Language, which defines a standard and standard for communication interfaces between clients and servers.

Why AIDL?

? We all know the four major components in android: Activity, Broadcast, Content Provider, and Service. We should have been familiar with the inter-process communication examples of the other three components except the Service, for example: an application can start another Activity explicitly. An application sends a broadcast and is accepted by other applications. An application provides a Content Provider, other applications then use ContentResolver to obtain the data it provides. These are examples of inter-process communication (usually each application runs in an independent Linux Process), so the Service component can also implement process communication, this is the AIDL Service introduced in this article. In addition to implementing services to provide services, AIDL also has an important reason:

Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL

This is a prompt from the official google documentation for AIDL, which means that "Only when you allow Process Communication from different applications through your service, AIDL must be used only when multiple threads are processed in your service. If you do not need to implement real-time process communication between different applications, you should create an interface to implement Binder, or, if you want to implement process communication but do not need to process multithreading, use a Messenger to implement your interface. However, you must first understand the local service before you implement AIDL.

Through the above sentence, we are very clear that the role of AIDL is to allow two different applications to communicate through the Service (Process Communication IPC), and remote services can process multithreading. Simply put, one application provides a remote Service, and other applications can concurrently access the Service, namely, the C/S mode.

Ii. AIDL simple example

Steps:

Create an AIDL file (with the extension. aidl); the server implements the Java interface generated by the AIDL file (the system automatically generates the corresponding Java Interface); exposes an interface to the client (by creating a Service, in onBind () method). The client connection is bound to the remote service.

Follow this step to understand the use of AIDL through code (this is based on the Android Studio tool, which is similar to (simpler) in eclipse )).

1. Create an AIDL File

? First, we need to create a new Module, which is a server application. You can directly create an AIDL file through the Module provided by the, this will automatically generate the aidl folder and the default AIDL file (the default AIDL package name is the application package name). It is a little troublesome to change the package name, because both the client and server must have the same AIDL file (the package name must also be the same), we can create an AIDL file manually in the following way, under the main folder (project view) create an aidl folder and create a package name under the folder. The package name is optional, but the AIDL file package name on the client and server must be consistent, next, create a file under the package name.IRemoteService.aidl.

The aidl file has its own syntax (aidl: Interface Definition Language ):

Each aidl file can only define one interface (single interface). The basic data types in Java are supported by default, such as int, long, char, and boolean. String and CharSequence are supported by default; list is supported by default (you can select to add a generic type and introduce the package where the List is located). However, the data stored in the List can only be of the Java basic data type, and the other side (client or server) the ArrayList type is accepted. Map is supported by default (you can add a generic type, but the generic type can only be a basic data type or String or CharSequence, and you need to introduce the package where Map is located, the data stored in Map can only be the basic Java data type, and the other side (client or server) accepts the HashMap type; all the annotations in the aidl file will appear in the automatically generated IBinder interface java file (except the annotation before the package statement is imported). The aidl interface only supports methods and does not support variables.

The aidl interface file writing method is very similar to the Java interface writing method, that is, do not use Public modifiers. Therefore, we write as follows:

IRemoteService. aidl

// IRemoteService.aidlpackage com.lt.aidl;// Declare any non-default types here with import statementsinterface IRemoteService {    /**     * 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);    /** Request the process ID of this service, to do evil things with it. */    int getPid();    /** get name by id */    String getName(int id);}

After Ctrl + S is saved, we can see that the compiler will automatically generate a corresponding project (if not, you can re-build this project)IRemoteService.javaFile, which is displayed in the Packages View:

We do not need to understand this automatically generated java file. Therefore, no matter what it is, we will proceed to the second and third steps, that is, the server implements the Java interface generated by the AIDL file (the system automatically generates the corresponding Java interface ).

2 (3). The server implements the Java interface generated by the AIDL file.

? Open the Java interface generated by AIDL and we can find that there is an internal static abstract class Stub, which inherits the Binder and implements this interface, we can directly use this Stub class to build remote services.

Create a new Sevice and return the Binder class implementing the AIDL Java interface in the onBind method (a new Stub class). This class will act as the Service proxy class:

Package com. lt. remoteservice. service; import android. app. service; import android. content. intent; import android. OS. IBinder; import android. OS. remoteException; import com. lt. aidl. IRemoteService;/*** Created by lt on 2016/3/6. */public class RemoteService extends Service {private String [] names = {"Lu Bu", "Guan Yu", "Zhao Zilong", "Zhang Fei "}; /*** return a RemoteService proxy object IBinder to use * @ param intent * @ return */@ Override public IBinder onBind (Intent intent) {return mBinder;} private final IRemoteService to the client. stub mBinder = new IRemoteService. stub () {@ Override public void basicTypes (int anInt, long aLong, boolean implements lean, float aFloat, double aDouble, String aString) throws RemoteException {System. out. println ("Thread:" + Thread. currentThread (). getName (); System. out. println ("basicTypes aDouble:" + aDouble + "anInt:" + anInt + "lean" + lean + "aString" + aString);} @ Override public int getPid () throws RemoteException {System. out. println ("Thread:" + Thread. currentThread (). getName (); System. out. println ("RemoteService getPid"); return android. OS. process. myPid () ;}@ Override public String getName (int id) throws RemoteException {return names [id] ;};}

Note: Do not forget to register the Service in the list file, and we also need to provide an intent-filter containing the action attribute (the client usually starts the Service through implicit intent ), the value of this action can be arbitrary.

4. Bind the client to the service

? To create another Module, you also need to create an aidl file. This file must be exactly the same as the aidl file on the server (note the package name). Copy the aidl folder on the server directly in this Module.

To intuitively display the communication effect, we provide an interactive interface, activity_main.xml

<Code class = "language-xml hljs"> <linearlayout xmlns: android = "http://schemas.android.com/apk/res/android" xmlns: tools = "http://schemas.android.com/tools" android: layout_width = "match_parent" android: orientation = "vertical" android: layout_height = "match_parent"> <edittext android: id = "@ + id/editText" android: hint = "input query ID" android: layout_width = "match_parent" android: layout_height = "wrap_content"> </edittext> </linearlayout> </code> <button android: onclick = "search" android: layout_width = "wrap_content" android: text = "query" android: layout_height = "wrap_content"> <code class = "language-xml hljs"> <linearlayout android: layout_width = "wrap_content" android: orientation = "horizontal" android: layout_height = "wrap_content"> <textview android: text = "Result:" android: layout_width = "wrap_content" android: layout_height = "wrap_content"> <textview android: id = "@ + id/TV _result" android: layout_width = "wrap_content" android: layout_height = "wrap_content"> </textview> </linearlayout> </code> </button>

The key point is to bind the client to a remote service and establish a service connection first.

Private ServiceConnection conn = new ServiceConnection () {@ Override public void onServiceConnected (ComponentName, IBinder service) {// here, the IBinder object service is a proxy object, therefore, you must call the following method to convert it to the AIDL interface object mRemoteService = IRemoteService. stub. asInterface (service); int pid = 0; try {pid = mRemoteService. getPid (); int currentPid = android. OS. process. myPid (); System. out. println ("currentPID:" + currentPid + "remoteP ID: "+ pid); mRemoteService. basicTypes (12,122 3, true, 12.2f, 12.3," Come on! ");} Catch (RemoteException e) {e. printStackTrace ();} System. out. println (" bind success! "+ MRemoteService. toString () ;}@ Override public void onServiceDisconnected (ComponentName name) {mRemoteService = null; System. out. println (mRemoteService. toString () +" disconnected! ");}};

Note:

Two Methods of connection: one is the callback after the service connection is successful, and the other is the callback when the server opens the connection. After the connection is successful, an IBinder object Service is returned in the onServiceConnected method. This service object must pass through IRemoteService.Stub.asInterface(service);Convert to AIDL object, which will be used as the proxy object of remote service.

Bind a remote service by implicit intent

// Connect to and bind the remote service Intent intent = new Intent (); // The action value is the action of the remote service, that is, the actionintent in the above list of application files on the server. setAction ("lt. test. aidl "); intent. setPackage ("com. lt. remoteservice "); isConnSuccess = bindService (intent, conn, Context. BIND_AUTO_CREATE );

Note:

In android 5.0, the implicit start of the service is restricted. You must set action and package. package refers to the package name of the service to be started, which is the package name of the application on the server; binding services may fail (for example, the AIDL files on the client and server are inconsistent ).

Interface interaction in response to the button event:

Public void search (View view) {if (isConnSuccess) {// connection successful int id = Integer. valueOf (mEditText. getText (). toString (); try {String name = mRemoteService. getName (id); mTv_result.setText (name);} catch (RemoteException ex) {ex. printStackTrace () ;}} else {System. out. println ("connection failed! ");}}

Complete client activity code:

Package com. lt. remoteclient; 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. IBinder; import android. OS. remoteException; import android. view. view; import android. widget. editText; import android. widget. textView; import com. lt. aidl. IRemoteService; Public class MainActivity extends Activity {private IRemoteService mRemoteService; private TextView mTv_result; private EditText mEditText; private boolean isConnSuccess; @ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); mEditText = (EditText) findViewById (R. id. editText); mTv_result = (TextView) findViewById (R. id. TV _result); // connect to and bind the remote service Intent = new intent (); Intent. setAction ("lt. test. aidl "); intent. setPackage ("com. lt. remoteservice "); isConnSuccess = bindService (intent, conn, Context. BIND_AUTO_CREATE);} public void search (View view) {if (isConnSuccess) {// connection successful int id = Integer. valueOf (mEditText. getText (). toString (); try {String name = mRemoteService. getName (id); mTv_result.setText (name);} ca Tch (RemoteException ex) {ex. printStackTrace () ;}} else {System. out. println ("connection failed! ") ;}} Private ServiceConnection conn = new ServiceConnection () {@ Override public void onServiceConnected (ComponentName, IBinder service) {// here, the IBinder object service is a proxy object, therefore, you must call the following method to convert it to the AIDL interface object mRemoteService = IRemoteService. stub. asInterface (service); int pid = 0; try {pid = mRemoteService. getPid (); int currentPid = android. OS. process. myPid (); System. out. println ("currentPID:" + currentPid +" RemotePID: "+ pid); mRemoteService. basicTypes (12,122 3, true, 12.2f, 12.3," Come on! ");} Catch (RemoteException e) {e. printStackTrace ();} System. out. println (" bind success! "+ MRemoteService. toString () ;}@ Override public void onServiceDisconnected (ComponentName name) {mRemoteService = null; System. out. println (mRemoteService. toString () +" disconnected! ") ;};@ Override protected void onDestroy () {super. onDestroy (); unbindService (conn );}}

Run the server application and then run the client application. Test Result:

Observe the background log printing:

As you can see, the server and client application are in two different processes, and the client can transmit basic types of data, and the client can also obtain data (query results) from the remote server application ), in this way, the communication between two applications is completed through the remote Service (the Process Communication IPC ). However, there is a problem. We just passed the data of the basic type but didn't pass the data of the non-basic type such as the object. Isn't it possible to pass the data of the non-basic type? The answer is yes, of course. The following describes how to transfer an object to a broken process.

3. AIDL kill the process to pass objects

? To pass non-basic data type objects through the AIDL kill process, this object needs to implement the Parcelable interface (this object type will be decomposed into basic data types in the android system, and then passed between processes ).

Implementation steps (create a class ):

Implement Parcelable interface; Overwrite writeTowriteToParcel(Parcel dest, int flags)ParcelThis method carries the status of the current object and writes it to Parcel; adds a static member variable CREATORThis variable is an implementation Parcelable.Creator interface.An aidl file corresponding to this class, that is, the file name is the same as the class name, and the extension is. aidl.

1 (2) (3). Create a class to implement the Parcelable Interface

Person. java

Package com. lt. aidl; import android. OS. parcel; import android. OS. parcelable;/*** Created by lt on 2016/3/8. */public class Person implements Parcelable {private String name; private int age; private Person (Parcel in) {readFromParcel (in);} private void readFromParcel (Parcel in) {this. name = in. readString (); this. age = in. readInt ();} public Person (String name, int age) {this. name = name; this. age = Age;}/*** the constant must be declared inside the entity class for serial number transfer. The constant name can only be a CREATOR, and the type must also be * Parcelable. Creator
  
   
T: The current object type */public static final Creator
   
    
CREATOR = new Creator
    
     
() {/*** According to the serialized Parcel object, deserialization of the original object * The read order must be the same as that of writeToParcel */@ Override public Person createFromParcel (Parcel in) {return new Person (in. readString (), in. readInt ();}/*** creates an array of entity classes to be serialized, all objects stored in the array are set to null */@ Override public Person [] newArray (int size) {return new Person [size] ;};@ Override public int describeContents () {return 0;}/*** write the object to Parcel (serialization) * @ param dest: the target object to be written * @ Param flags: ID of the serial number of the object * Note that the write order is exactly the same as that read in the createFromParcel method. For example, if name is first written here, * in createFromParcel, you must first read name */@ Override public void writeToParcel (Parcel dest, int flags) {dest. writeString (name); dest. writeInt (age);} public int getAge () {return age;} public String getName () {return name ;}}
    
   
  

4. Create the aidl file corresponding to this class

Create a corresponding aidl file in the package of the Person classPerson.aidl, File content:

package com.lt.aidl;// Declare Rect so AIDL can find it and knows that it implements// the parcelable protocol.parcelable Person;

The current project structure is as follows:

OK.Person.javaAndPerson.aidlCopy a copy to the same place on the client, and then re-build the entire project. If Android Studio reports an error in the import package during the build process (AIDL custom type import failed ), it took me a few hours to solve this problem ...), Solution:

Add the following code to the project grade file to use AIDL as the source folder:

sourceSets {    main {        manifest.srcFile 'src/main/AndroidManifest.xml'        java.srcDirs = ['src/main/java', 'src/main/aidl']        resources.srcDirs = ['src/main/java', 'src/main/aidl']        aidl.srcDirs = ['src/main/aidl']        res.srcDirs = ['src/main/res']        assets.srcDirs = ['src/main/assets']    }}

After this code is added, the content of the entire grade file is:

apply plugin: 'com.android.application'android {    compileSdkVersion 23    buildToolsVersion "23.0.1"    defaultConfig {        applicationId "com.lt.remoteclient"        minSdkVersion 11        targetSdkVersion 23        versionCode 1        versionName "1.0"    }    sourceSets {        main {            manifest.srcFile 'src/main/AndroidManifest.xml'            java.srcDirs = ['src/main/java', 'src/main/aidl']            resources.srcDirs = ['src/main/java', 'src/main/aidl']            aidl.srcDirs = ['src/main/aidl']            res.srcDirs = ['src/main/res']            assets.srcDirs = ['src/main/assets']        }    }    buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }}dependencies {    compile fileTree(dir: 'libs', include: ['*.jar'])    compile 'com.android.support:appcompat-v7:23.0.1'}

Then re-build the project, OK, perfect solution (exciting ...), Test results after running two applications:

Source code.

Here, AIDL is used up.

Summary:

? As an Interface Definition Language, AIDL has its own syntax. Its syntax is similar to the definition of a java interface, so someone also creates an interface, then, change the suffix "aidl" to create the AIDL interface file. Note that AIDL is used only when different applications need to communicate and process multiple threads, otherwise, other processes can be used for communication. In addition, AIDL supports most data types by default. However, to transmit object data, you must take some measures.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.