Tag:AIDL asynchronous
Category:Android
Article From:https://www.cnblogs.com/cspecialy/p/9062384.html

AIDL:Android Interface Definition Language,That is, the Android interface definition language.

AIDL What’s this

Android No memory can be shared between processes in the system, so some mechanisms need to be provided for data communication between different processes.

In order to enable other applications to access the services provided by the application, the Android system uses a remote process call (Remote Procedure Call, RPC). Like many other RPC based solutions, ANdroid uses a Interface Definition Language (IDL) to expose the interface of the service. We know 3 of the four major components of Android (Activity, Broadc).Both astReceiver and ContentProvider can cross process access, and the other Android component Service can also. Therefore, you can call this kind of service that can cross process access AIDL (Androi).D Interface Definition Language) service.

Before introducing the use and other features of AIDL, let’s first understand the core of AIDL.Binder

Android Analysis of the Binder mechanism

Looking at some of the articles about Binder, the underlying implementation of the Binder mechanism is complex and complex, and it takes a lot of time to make it clear. In some ways, individuals feel that for Binder, we only need to understand its upper principles and use.The method can.

Intuitively, from the point of view of the code, Binder is a class in the Android system source code, which implements the IBinder interface; from the IPC point of view, Binder is a cross process communication mode in Android; from ANdroid Framework point of view, Binder is ServiceManager to connect all kinds of Manager (ActivityManager, WindowManager, etc.) and corresponding Mana.The bridge of gerService; from the Android application layer, Binder is the medium of communication between the client and the server, and when bindService, the server will return a Binder pair that contains the service call of the server.Like this, through the Binder object, the client can communicate with the server. The services here include common services and AIDL based services.

Next, we analyze the working mechanism of Binder through an example of AIDL. In the project directory, create a new package named Aidl and then create a new Book.Java and Book.aidl.The hint already exists. You need to use other commands to create a success and rename it to Book.aidl) and IBookManager.aidl. The code is as follows:

// Book.java
package com.cy.ipcsample.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Data class* @author cspecialy* @version v1.0.0* @date 2018/5/14 21:38* * /Public class Book implementS Parcelable {Public int bookId;Public String bookName;Public Book (int bookId, StriNg bookName) {This.bookId = bookId;This.bookName = bookName;}ProtectedBook (Parcel in) {BookId = in.readInt ();BookName = in.readString ();}PublIC static final Creator< Book> CREATOR = new Creator< Book> () {@OverridePublic Book createFromParcel (Parcel in) {Return new Book (in);}@OverrIDEPublic Book[] newArray (int size) {Return new Book[size];}};@OverridePublic int describeContents () {Return 0;}@OverridePublicVoid writeToParcel (Parcel parcel, int i) {Parcel.writeInt (bookId);Parcel.writeStriNg (bookName);}@OverridePublic String toString () {Return "Book{" +"BookId=" + bookId +", bookName='" + bookName +' \ '+'s';}}
1 // Book.aidl
2 package com.cy.ipcsample.aidl;
3 
4 parcelable Book;/
 1 // IBookManager.aidl
 2 package com.cy.ipcsample.aidl;
 3 
 4 import com.cy.ipcsample.aidl.Book;
 5 import com.cy.ipcsample.aidl.IOnNewBookArrivedListener;
 6 
 7 interface IBookManager {
 8     List<Book> getBookList();
 9     void addBook(in Book book);
10     void registerListener(IOnNewBookArrivedListener listener);
11     void unRegisterListener(IOnNewBookArrivedListener listener);
12 }
After creating three files, compile it and as will be there.app/build/generated/source/aidl/debug A class named IBookManager.Java is generated in the com.cy.ipcsample package under the directory, as shown in the following figure:

 

This is the Binder class generated by the system. Next we need to use this class to analyze the working principle of Binder. Its code is as follows: (generated code is very messy, can be formatted after the code to see)

  1 /*
  2  * This file is auto-generated.  DO NOT MODIFY.
  3  * Original file: G:\\Android\\Github\\Bugly-Android-Demo\\sample\\ipcsample\\src\\main\\aidl\\com\\cy\\ipcsample
  4  * \\aidl\\IBookManager.aidl
  5  */
  6 package com.cy.ipcsample.aidl;
  7 
  8 public interface IBookManager extends android.os.IInterface {
  9     public java.util.List<com.cy.ipcsample.aidl.Book> getBookList() throws android.os.RemoteException;
 10 
 11     public void addBook(com.cy.ipcsample.aidl.Book book) throws android.os.RemoteException;
 12 
 13     /** Local-side IPC implementation stub class. */
 14     public static abstract class Stub extends android.os.Binder implements com.cy.ipcsample.aidl.IBookManager {
 15         static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
 16         static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
 17         private static final java.lang.String DESCRIPTOR = "com.cy.ipcsample.aidl.IBookManager";
 18 
 19         /** Construct the stub at attach it to the interface. */
 20         public Stub() {
 21             this.attachInterface(this, DESCRIPTOR);
 22         }
 23 
 24         /**
 25          * Cast an IBinder object into an com.cy.ipcsample.aidl.IBookManager interface,
 26          * generating a proxy if needed.
 27          */
 28         public static com.cy.ipcsample.aidl.IBookManager asInterface(android.os.IBinder obj) {
 29             if ((obj == null)) {
 30                 return null;
 31             }
 32             android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
 33             if (((iin != null) && (iin instanceof com.cy.ipcsample.aidl.IBookManager))) {
 34                 return ((com.cy.ipcsample.aidl.IBookManager) iin);
 35             }
 36             return new com.cy.ipcsample.aidl.IBookManager.Stub.Proxy(obj);
 37         }
 38 
 39         @Override
 40         public android.os.IBinder asBinder() {
 41             return this;
 42         }
 43 
 44         @Override
 45         public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws 
 46                 android.os.RemoteException {
 47             switch (code) {
 48                 case INTERFACE_TRANSACTION: {
 49                     reply.writeString(DESCRIPTOR);
 50                     return true;
 51                 }
 52                 case TRANSACTION_getBookList: {
 53                     data.enforceInterface(DESCRIPTOR);
 54                     java.util.List<com.cy.ipcsample.aidl.Book> _result = this.getBookList();
 55                     reply.writeNoException();
 56                     reply.writeTypedList(_result);
 57                     return true;
 58                 }
 59                 case TRANSACTION_addBook: {
 60                     data.enforceInterface(DESCRIPTOR);
 61                     com.cy.ipcsample.aidl.Book _arg0;
 62                     if ((0 != data.readInt())) {
 63                         _arg0 = com.cy.ipcsample.aidl.Book.CREATOR.createFromParcel(data);
 64                     } else {
 65                         _arg0 = null;
 66                     }
 67                     this.addBook(_arg0);
 68                     reply.writeNoException();
 69                     return true;
 70                 }
 71             }
 72             return super.onTransact(code, data, reply, flags);
 73         }
 74 
 75         private static class Proxy implements com.cy.ipcsample.aidl.IBookManager {
 76             private android.os.IBinder mRemote;
 77 
 78             Proxy(android.os.IBinder remote) {
 79                 mRemote = remote;
 80             }
 81 
 82             public java.lang.String getInterfaceDescriptor() {
 83                 return DESCRIPTOR;
 84             }            @Override
 85             public android.os.IBinder asBinder() {
 86                 return mRemote;
 87             }
 88 
 89             @Override
 90             public java.util.List<com.cy.ipcsample.aidl.Book> getBookList() throws android.os.RemoteException {
 91                 android.os.Parcel _data = android.os.Parcel.obtain();
 92                 android.os.Parcel _reply = android.os.Parcel.obtain();
 93                 java.util.List<com.cy.ipcsample.aidl.Book> _result;
 94                 try {
 95                     _data.writeInterfaceToken(DESCRIPTOR);
 96                     mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
 97                     _reply.readException();
 98                     _result = _reply.createTypedArrayList(com.cy.ipcsample.aidl.Book.CREATOR);
 99                 } finally {
100                     _reply.recycle();
101                     _data.recycle();
102                 }
103                 return _result;
104             }
105 
106             @Override
107             public void addBook(com.cy.ipcsample.aidl.Book book) throws android.os.RemoteException {
108                 android.os.Parcel _data = android.os.Parcel.obtain();
109                 android.os.Parcel _reply = android.os.Parcel.obtain();
110                 try {
111                     _data.writeInterfaceToken(DESCRIPTOR);
112                     if ((book != null)) {
113                         _data.writeInt(1);
114                         book.writeToParcel(_data, 0);
115                     } else {
116                         _data.writeInt(0);
117                     }
118                     mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
119                     _reply.readException();
120                 } finally {
121                     _reply.recycle();
122                     _data.recycle();
123                 }
124             }
125 
126 
127         }
128     }
129 }

It can be seen that the system has created a IBookManager interface for us, which inherits the IInterface interface, so here it is to be noted that all interfaces that can be transmitted in Binder need to inherit the IInterface interface.

Next, the working mechanism of the class is analyzed. Looking closely, it can be found that the class is mainly divided into three parts.

  • Define your own methods (getBookList method and addBook method);
  • The internal static class -Stub, which inherits Binder and implements IBookManager interface.
  • StubThe internal proxy class -Proxy also implements the IBookManager interface.

In the first part, we need not control it, mainly looking at Stub and Proxy. In Stub, we first declare two integer variables used to identify the IBookManager method. These two variables are used to identify the client in the transact process.Which method is requested. Then, the asInterface method is used to convert the server – side Binder object into an object of the AIDL interface type required by the client, which is called by calling the queryLocalI of the BinderThe nterface method determines whether the client and the server are in the same process. If the client and server are in the same process, then this method returns the Stub of the server directly, otherwise, the agent object Proxy of the Stub is returned. QueryLocalInThe implementation of terface is as follows:

/**
 * Use information supplied to attachInterface() to return the
 * associated IInterface if it matches the requested
 * descriptor.
 */
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
    if (mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}

mOwner Is the this parameter passed in the Stub constructor.

Next, Stub’s proxy class Stub.Proxy, from the above analysis, is that Stub.Proxy is running on the client side (the object returned to the client by the asInterface method), and after the Stub.Proxy object is created, it is heldThe Binder object of the server is used to invoke the server side method for remote calls when the client requests. When the client initiates a request to the server, it calls the corresponding method of Stub.Proxy. The flow of the Stub.Proxy method is as follows:

  • First, create the input Parcel object _data, output object _reply and return value object (if any) needed by the method.
  • Then the parameter information of the method is written to _data (if there is).
  • The transact method is then called for RPC (remote procedure call) and the current thread is suspended.
  • Then, in the transact method, the onTransact method of the service side is called through the Binder object of the service side, that is, the onTransact method in Stub.
  • onTransact After the method returns, the current thread continues to execute, and the result returned from the RPC procedure is extracted from _reply.
  • Finally, the data in _reply is returned (if the client request method needs to return value).

The above is the working process of the IBookManager generated by the system. We need to notice that the onTransact method of the server is running in the Binder thread pool. Because the IBookManager.Stub class inherits BinDer, so the above analysis is the working mechanism of Binder.

  • When the client is connected to the server (generally through the bindService method), the server returns an object of the IInterface interface type to the client through the asInterface method (if the client and server are in the same process, the client returns to the same process.The Binder object of the server side, otherwise, the agent of the Binder object of the service side will be returned.
  • The client requests the server through this object. If the client and the server are not in the same process, the RPC request is carried out through the Binder object that the object is acting. Otherwise, the server is called directly through the Binder object.

Or refer to the picture below:

 

Thus, Binder has an important function in AIDL, the core of AIDL, and the understanding of the working mechanism of Binder, which is actually very useful in many ways.

AIDL Use

The steps to build a set of AIDL services are as follows:

  • Create the.Aidl file, and the system generates the corresponding interface class that inherits IInterface (exposed to the client interface).
  • Create a Service (server side) to implement the interface in the.Aidl file.
  • Create the client, bind the Service of the server side.
  • After the client is bound to the server successfully, the Binder object returned by the server is transformed into the IInterface type belonging to the AIDL interface. The method in AIDL is invoked to communicate with the server.

Next, we use the IBookManager above to implement AIDL.

  1. Create a.Aidl file
    Directly use the Book.aidl and IBookManager.aidl files created above.
  2. Create a server side
    Create a Service named BookManagerService. The code is as follows:

     1 package com.cy.ipcsample.aidl
     2 
     3 import android.app.Service
     4 import android.content.Intent
     5 import android.os.IBinder
     6 import android.os.RemoteCallbackList
     7 import android.util.Log
     8 import java.util.concurrent.CopyOnWriteArrayList
     9 
    10 class BookManagerService : Service() {
    11     private val TAG = "BookManagerService"
    12 
    13     private val mBookList = CopyOnWriteArrayList<Book>()
    14     private val mListenerList = RemoteCallbackList<IOnNewBookArrivedListener>()
    15 
    16     /**
    17      * The Binder object that implements the AIDL interface directly returns the Binder object when the client binds the server.18      */
    19     private val mBinder = object : IBookManager.Stub() {
    20 
    21         override fun getBookList(): MutableList<Book> {
    22             return mBookList
    23         }
    24 
    25         override fun addBook(book: Book?) {
    26             mBookList.add(book)
    27         }
    28 
    29     }
    30 
    31     override fun onBind(intent: Intent): IBinder {
    32         return mBinder
    33     }
    34 
    35     override fun onCreate() {
    36         super.onCreate()
    37 
    38         // Create two books
    39         mBookList.add(Book(1, "Android"))
    40         mBookList.add(Book(2, "iOS"))
    41     }
    42 }

    Then register Service in AndroidManifest, pay attention to start multi process:

    <service
        android:name=".aidl.BookManagerService"
        android:process="com.cy.ipcsample.bookManagerService">
    </service>
  3. Service of client bound server side
    The client is created directly using Activity, and the code for binding remote services is as follows:

    private val mConnection = object : ServiceConnection {
    
        override fun onServiceDisconnected(name: ComponentName?) {
        }
    
        /**
         * Connecting a successful callback to a remote service*/
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        }
    
    }
    
    // Bind remote service
    bindService(Intent(this, BookManagerService::class.java),
            mConnection, Context.BIND_AUTO_CREATE)
  4. Server-side communication
    After the success of the server binding, first in the ServiceConnection callback, the Binder object returned by the server is converted to the object of the AIDL interface, and the corresponding method and the server can be called. The code is as follows:

    // Converts the Binder object returned by the server to the IBookManager object.
    val bookManager = IBookManager.Stub.asInterface(service)
    
    // Server-side communication
    try {
        // Get a list of books
        val list = bookManager.bookList
        Log.i(TAG, "query book list, list type: ${list.javaClass.canonicalName}")
        Log.i(TAG, "query book list: $list")
    
        // Add a Book
        val book = Book(3, "AndroidExploration of the development of art ")
        bookManager.addBook(book)
        Log.i(TAG, "add book: $book")
    
        // Get a list of books
        val newList = bookManager.bookList
        Log.i(TAG, "query book list: $newList")
    } catch (e: RemoteException) {
        e.printStackTrace()
    }

    In the code, we first queried the list of books on the server side, and then added a book to the server.AndroidExploration of art development,Then check again to see if the addition is successful. Run log, as shown in the following figure:

    It can be seen that the running results are in accordance with the expected results. The complete client code is as follows:

     1 package com.cy.ipcsample.aidl
     2 
     3 import android.content.ComponentName
     4 import android.content.Context
     5 import android.content.Intent
     6 import android.content.ServiceConnection
     7 import android.os.Bundle
     8 import android.os.IBinder
     9 import android.os.RemoteException
    10 import android.support.v7.app.AppCompatActivity
    11 import android.util.Log
    12 import com.cy.ipcsample.R
    13 
    14 class BookManagerActivity : AppCompatActivity() {
    15 
    16     private val TAG = "BookManagerActivity"
    17 
    18     private val mConnection = object : ServiceConnection {
    19 
    20         override fun onServiceDisconnected(name: ComponentName?) {
    21             Log.d(TAG, "binder died.")
    22         }
    23 
    24         /**
    25          * Connecting a successful callback to a remote service26          */
    27         override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
    28             // Converts the Binder object returned by the server to the IBookManager object.
    29             val bookManager = IBookManager.Stub.asInterface(service)
    30 
    31             // Server-side communication
    32             try {
    33                 // Get a list of books
    34                 val list = bookManager.bookList
    35                 Log.i(TAG, "query book list, list type: ${list.javaClass.canonicalName}")
    36                 Log.i(TAG, "query book list: $list")
    37 
    38                 // Add a Book
    39                 val book = Book(3, "AndroidExploration of the development of art ")
    40                 bookManager.addBook(book)
    41                 Log.i(TAG, "add book: $book")
    42 
    43                 // Get a list of books
    44                 val newList = bookManager.bookList
    45                 Log.i(TAG, "query book list: $newList")
    46             } catch (e: RemoteException) {
    47                 e.printStackTrace()
    48             }
    49         }
    50 
    51     }
    52 
    53     override fun onCreate(savedInstanceState: Bundle?) {
    54         super.onCreate(savedInstanceState)
    55         setContentView(R.layout.activity_book_manager)
    56 
    57         // Bind remote service
    58         bindService(Intent(this, BookManagerService::class.java),
    59                 mConnection, Context.BIND_AUTO_CREATE)
    60     }
    61 
    62     override fun onDestroy() {
    63         unbindService(mConnection)
    64         super.onDestroy()
    65     }
    66 }

Here, a simple AIDL example is done, of course, the use of AIDL is far from simple, and there are many situations to consider, such as: the client needs the server at any time to change the client at the same time when the state changes, so it is similar to observing this mode, then how to subscribe and anti subscribe to the client; BiNder accidental death, how to reconnect and so on, more content, can refer to “Android art development exploration”, e-book download.

AIDL Asynchronous invocation

AIDL Is the calling process synchronous or asynchronous?

This problem is actually seen in the title of this section, and the calling process of AIDL is synchronous. At the same time, the above analysis of the mechanism of Binder, also mentioned that when the client performs remote RPC requests, the thread will hang up, wait for the result, so it can also be known that the AIDL is in tune.The process is synchronized, and the following is verified.

First, the getBookList method of the Binder object implemented in the BookManagerService of the server is added to the delay execution, as shown in the following figure:

 

Then, add a button to the BookManagerActivity of the client. When you click the button, call the getBookList of the server Binder to make RPC request. The code is as follows:

 

After running, click the button continuously, and the result is as follows:

As you know, after a continuous click of the button, there is no response error (ANR). As a result, when the client makes RPC requests to the server, it is synchronous, that is to say:AIDL The invocation process is synchronized

 

AIDL The calling process is synchronous. When we need the server to do time consuming operations, it is certainly not a synchronous call, otherwise the light affects the user experience, the heavy ANR or the application crashes. So how do we make the calling process of AIDL asynchronous?

In fact, it is also very simple. You only need to call the non UI thread. If you want to update the UI of the call, then you can do it by Handler. As shown in the following figure:

 

Reference to this article

  • 《AndroidThe second chapter of the exploration of the art of development

Leave a Reply

Your email address will not be published. Required fields are marked *