An overview,

AIDL stands for Android Interface Definition Language. AIDL is a description Language used to define the communication Interface between the server and the client, which can be used to generate code for IPC. AIDL is actually a template in a sense, because it’s not an AIDL file that actually works, it’s an instance of IInterface code that’s generated from it, and AIDL is a template that’s created to keep us from writing code again and again

AIDL was designed to enable interprocess communication. In Android, each process runs in a separate block of memory where it performs its activities, separated from other processes. But sometimes we have a need to interact with each other, to compare and transfer data or delegate tasks, and AIDL was created to meet this need. With AIDL, you can retrieve data from one process and invoke exposed methods from another process to meet the needs of interprocess communication

Generally, the application that exposes methods to call other applications is called the server, and the application that calls methods of other applications is called the client. The client interacts by binding the Service of the server

Second, the grammar

The syntax of AIDL is very simple and basically the same as that of the Java language. Here are some rules to keep in mind:

  1. AIDL files have the.aidl suffix
  2. AIDL supports the following data types:
    • Eight basic data types: byte, char, short, int, long, float, double, Boolean
    • String, CharSequence
    • Implements the data type of the Parcelable interface
    • The List type. The data carried by the List must be of a type supported by AIDL, or another declared AIDL object
    • The Map type. The data hosted by the Map must be of the type supported by AIDL or other declared AIDL objects
  3. AIDL files fall into two categories. A class is used to declare data types that implement the Parcelable interface so that other AIDL files can use data types that are not supported by default. Another class defines interface methods, declaring which interfaces to expose for client calls. Directional tags are used to label the parameter values of these methods
  4. Orientation of the Tag. Directional Tag represents the flow direction of data in cross-process communication, and is used to mark the parameter values of methods, including in, OUT and InOUT. In indicates that data flows only from the client to the server, out indicates that data flows only from the server to the client, and inout indicates that data flows bidirectional between the server and the client. In addition, if the parameter value type of the AIDL method interface is: For basic data types, String, CharSequence, or other method interfaces defined in AIDL files, the default Tag for these parameter values is in, so all parameter values except these types need to be clearly marked with which Tag to use. Specific differences in the use of directional tags will be described later
  5. Clear guide package. You need to specify the package name of the referenced data type in an AIDL file, even if both files are under the same package name

Server coding

Here is a practical example as a demonstration, the function to be realized is: the client invokes the method of the server by binding the Service of the server, obtains the book list of the server and adds books to it, and realizes data sharing between applications

First of all, we need to use a Book class in the application, and the Book class is used between the two applications, so we also need to declare the Book class in the AIDL file. To avoid the error of creating a file with duplicate class names, you need to create the Book AIDL file first and then the Book class

Right click on a new AIDL file and name it Book

By default, an aiDL folder is created. The directory structure in this folder is the name of the project package, and the book. aidi file is in it

There is a default method in the book.aidl file that can be deleted

Now you can define the Book class, which contains only a Name attribute and implements the Parcelable interface

*/ public class Book implements Parcelable {private String implements Parcelable; public Book(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @override public String toString() {return "book name: "+ name; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.name); } public void readFromParcel(Parcel dest) { name = dest.readString(); } protected Book(Parcel in) { this.name = in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel source) { return new Book(source); } @Override public Book[] newArray(int size) { return new Book[size]; }}; }Copy the code

Now modify the book.aidl file to declare the Parcelable data type aiDL file

package com.czy.server;

parcelable Book;
Copy the code

In addition, according to the original assumption, the server needs to expose the client with a method to get a list of books and a method to add books, which are first defined in an AIDL file named BookController.aidl, and note that the package needs to be explicit here

package com.czy.server;
import com.czy.server.Book;

interface BookController {

    List<Book> getBookList();

    void addBookInOut(inout Book book);

}
Copy the code

As mentioned above, it is not the AIDL files that really matter in interprocess communication, but the files generated by the system, which can be viewed in the following directory. Then you need to use the internal static abstract class Stub

After creating or modifying AIDL files, we need to clean the project so that the system can generate the files we need in time

Now you need to create a Service for the client to bind to remotely, named AIDLService

/** * Author: Ye Ying Shi Ye * Time: 2017/8/26 0:07 * description:  */ public class AIDLService extends Service { private final String TAG = "Server"; private List<Book> bookList; public AIDLService() { } @Override public void onCreate() { super.onCreate(); bookList = new ArrayList<>(); initData(); } private void initData() {Book book1 = new Book(" alive "); Book2 = new Book(" or "); Book book3 = new Book(" leaf should be leaf "); Book book4 = new Book("https://github.com/leavesC"); Book book5 = new Book("http://www.jianshu.com/u/9df45b87cfdf"); Book book6 = new Book("http://blog.csdn.net/new_one_object"); bookList.add(book1); bookList.add(book2); bookList.add(book3); bookList.add(book4); bookList.add(book5); bookList.add(book6); } private final BookController.Stub stub = new BookController.Stub() { @Override public List<Book> getBookList() throws RemoteException { return bookList; } @Override public void addBookInOut(Book book) throws RemoteException { if (book ! = null) {book.setName(" server changed the name of the new book InOut"); bookList.add(book); } else {log. e(TAG, "received an empty object InOut"); }}}; @Override public IBinder onBind(Intent intent) { return stub; }}Copy the code

As you can see, the onBind method returns the BookController.stub object that implements the two methods defined in the onBind method

Finally, there is another important note on the server side. Because the Service on the server side needs to be bound remotely by the client, the client can find the Service by specifying the package name. If you want to bind the Service to the Action value, you need to change the declaration of the Service to look like this:

This example uses the Action configuration scheme

4. Client coding

The client needs to create a new project with the package name com.czy.client

First, you need to copy the server AIDL file and the Book class, and copy the entire AIDL folder to the same level as the Java folder without changing any code

After that, you need to create the same package name as the Book class on the server

Modify the layout file by adding two buttons

<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <Button android:id="@+id/btn_getBookList" android:layout_width="match_parent" android:layout_height="wrap_content" <Button android:id="@+id/btn_addBook_inOut" Android :layout_width="match_parent" Android :layout_height="wrap_content" Android :text="InOut add books "/> </LinearLayout>Copy the code
/** * Author: Ye Ying Shi Ye * Time: 2017/8/26 0:34 * description:  */ public class MainActivity extends AppCompatActivity { private final String TAG = "Client"; private BookController bookController; private boolean connected; private List<Book> bookList; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bookController = BookController.Stub.asInterface(service); connected = true; } @Override public void onServiceDisconnected(ComponentName name) { connected = false; }}; private View.OnClickListener clickListener = new View.OnClickListener() { @Override public void onClick(View v) { switch  (v.getId()) { case R.id.btn_getBookList: if (connected) { try { bookList = bookController.getBookList(); } catch (RemoteException e) { e.printStackTrace(); } log(); } break; Case r.i.btn_addbook_inout: if (connected) {Book Book = new Book(" This is a new Book InOut"); try { bookController.addBookInOut(book); Log.e(TAG, "added a new book to the server InOut "); Log.e(TAG, "new title:" + book.getName()); } catch (RemoteException e) { e.printStackTrace(); } } break; }}}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn_getBookList).setOnClickListener(clickListener); findViewById(R.id.btn_addBook_inOut).setOnClickListener(clickListener); bindService(); } @Override protected void onDestroy() { super.onDestroy(); if (connected) { unbindService(serviceConnection); } } private void bindService() { Intent intent = new Intent(); intent.setPackage("com.czy.server"); intent.setAction("com.czy.server.action"); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } private void log() { for (Book book : bookList) { Log.e(TAG, book.toString()); }}}Copy the code

The two buttons are used to get the list of books on the server and to add a Book. When adding a Book, the server also changes the Name property of the Book object to observe changes in the data on the client and server

First click to get the book list, the data is correct

Click the button to add the book, and you can see that the changes made by the server are synchronized to the client side

At this point, the communication between the client and the server is complete. The client gets the data from the server and sends the data to the server

5. Directional Tag

Finally, I’ll talk about the differences between the three targeted tags. Using the InOut type, changes made by the server are synchronized to the client, so data flows in both directions

In: Data can only be transmitted from the client to the server. The modification of data by the server does not affect the client

The representation of the Out type is that data can only be passed from the server to the client. Even if the client passes an object to the method interface, the attribute value in the object is empty, that is, it does not contain any data. After the server obtains the object, any operation on the object will be synchronized to the client

So let’s do a real demonstration here

Start by modifying the bookController.aidl file on the server and adding two new methods to it

package com.czy.server;
import com.czy.server.Book;

interface BookController {

    List<Book> getBookList();

    void addBookInOut(inout Book book);

    void addBookIn(in Book book);

    void addBookOut(out Book book);

}
Copy the code

The bookController.stub object of the AIDLService class needs to be modified as follows:

private final BookController.Stub stub = new BookController.Stub() { @Override public List<Book> getBookList() throws RemoteException { return bookList; } @Override public void addBookInOut(Book book) throws RemoteException { if (book ! = null) {book.setName(" server changed the name of the new book InOut"); bookList.add(book); } else {log. e(TAG, "received an empty object InOut"); } } @Override public void addBookIn(Book book) throws RemoteException { if (book ! = null) {book.setName(" server changed the name of the new book In"); bookList.add(book); } else {log. e(TAG, "received an empty object In"); } } @Override public void addBookOut(Book book) throws RemoteException { if (book ! = null) {log.e (TAG, "+ book.getName()); Book.setname (" the server changed the name of the new book Out"); bookList.add(book); } else {log. e(TAG, "received an empty object Out"); }}};Copy the code

Synchronize changes to the bookController.aidl file on the client

Add two more buttons to the layout file, each for adding different targeted Tag data

private View.OnClickListener clickListener = new View.OnClickListener() { @Override public void onClick(View v) { switch  (v.getId()) { case R.id.btn_getBookList: if (connected) { try { bookList = bookController.getBookList(); } catch (RemoteException e) { e.printStackTrace(); } log(); } break; Case r.i.btn_addbook_inout: if (connected) {Book Book = new Book(" This is a new Book InOut"); try { bookController.addBookInOut(book); Log.e(TAG, "added a new book to the server InOut "); Log.e(TAG, "new title:" + book.getName()); } catch (RemoteException e) { e.printStackTrace(); } } break; Case r.i.btn_addbook_in: if (connected) {Book Book = new Book(" This is a new Book In"); try { bookController.addBookIn(book); Log.e(TAG, "added a new book to the server In "); Log.e(TAG, "new title:" + book.getName()); } catch (RemoteException e) { e.printStackTrace(); } } break; Case r.i.btn_addbook_out: if (connected) {Book Book = new Book(" This is a new Book Out"); try { bookController.addBookOut(book); Log.e(TAG, "added a new book to the server Out "); Log.e(TAG, "new title:" + book.getName()); } catch (RemoteException e) { e.printStackTrace(); } } break; }}};Copy the code

In addition, there is one area that needs to be modified, namely the Book class. Because the Out type does cause the client to pass an object back to the server that contains no data, but that object is not null, the system still needs to instantiate the Book class. Currently, the Book class has only one argument constructor, so we need to modify the Book class. Add a no-parameter constructor for use by the system

Click the three buttons respectively, and you can see that the server does not contain the title property value when it gets the Book object from the client in Out mode

This is the end of AIDL. The above code is also available here. For convenience, I have saved the client-side code into a compressed file and put it in the server-side project file

Download the Demo:Android AIDL usage details