Android cross process communication Binder workflow
This article only describes Binder's workflow, not Binder's principle
-
First, what is AIDL
AIDL is the Android Interface definition language
-
AIDL and Binder action
AIDL is based on the Binder mechanism. Using AIDL, the SDK can automatically generate the corresponding Binder classes for us.
Binder is a bridge for communication between different processes. -
Get Binder implementation class using AIDL
-
Create an AIDL file using Android Studio
new->AIDL
-
The instance name is ibookmanager aidl
Custom types in Aidl need to be imported using import, and custom classes need to implement serialization interfaces (serializable or Parcelable) and be declared using a separate Aidl file with the same name, such as the Book used in the following example
package com.example.ipctest.aidl; import com.example.ipctest.aidl.Book; import com.example.ipctest.aidl.IOnNewBookArrivedListener; // Declare any non-default types here with import statements interface IBookManager { List<Book> getBookList(); //Get books void addBook(in Book book); //Add books } //Book.aidl package com.example.ipctest.aidl; // Declare any non-default types here with import statements parcelable Book; //Book.java public class Book implements Parcelable { public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } protected Book(Parcel in) { bookId = in.readInt(); bookName = in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeString(bookName); } }
-
You can build a project in build - > generated - > Aidl_ source_ output_ Find the automatically generated IBookManager interface in dir - > debug directory
The main components of this interface
- A static class Default implemented by Default
- A static abstract class Stub inherits Binder and implements this interface. This is Binder. At the same time, it has a static internal class Proxy. You can think of Stub as a server and Proxy as a client.
- The method previously declared in AIDL and the code value corresponding to the method
//All interfaces that can be transmitted in the binder need to inherit IInterface public interface IBookManager extends android.os.IInterface { /** IBookManager Interface */ public static class Default implements IBookManager { @Override public java.util.List<Book> getBookList() throws android.os.RemoteException { return null; } @Override public void addBook(Book book) throws android.os.RemoteException { } @Override public android.os.IBinder asBinder() { return null; } } //This is equivalent to a binder, which inherits from the binder class and implements this interface public static abstract class Stub extends android.os.Binder implements IBookManager { //The unique identifier of the binder is generally identified by the class name of the current binder private static final String DESCRIPTOR = "com.example.ipctest.aidl.IBookManager"; // Default constructor, attaching Stub to interface public Stub() { this.attachInterface(this, DESCRIPTOR); } /** Static method * Convert the Binder object of the server into the AIDL interface type object required by the client. This conversion process is process specific. If the client and the server belong to the same process * ,Then this method returns the server Stub object itself, otherwise it returns the system encapsulated Stub Proxy object * After obtaining the converted object, you can execute the required method */ public static IBookManager asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } //The query implements the local instance of the Binder object. If it is null, it means that the client and the server do not belong to the same process, so you need to create a proxy object and return it to the client android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof IBookManager))) { return ((IBookManager)iin); } //The client and server do not use proxy to wrap and return Binder in the same process. Proxy also implements IBookManager interface return new IBookManager.Stub.Proxy(obj); } //Returns the current binder object @Override public android.os.IBinder asBinder() { return this; } //This method runs in the Binder thread pool. When the client initiates a cross process request, the remote request will be encapsulated at the bottom of the system and handed over to this method for processing //When calling a remote service method, first execute the corresponding method in the Proxy, execute the onTransact() method of the object inheriting the Stub class through transact(), and call back the corresponding interface according to the code value in the onTransact method. At this time, the whole method execution process is roughly ended @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_basicTypes: { data.enforceInterface(descriptor); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0!=data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } case TRANSACTION_getBookList: { data.enforceInterface(descriptor); //Execute the getBookList() method implemented on the server side java.util.List<Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(descriptor); Book _arg0; if ((0!=data.readInt())) { _arg0 = Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } //Execute the addBook() method implemented on the server this.addBook(_arg0); reply.writeNoException(); return true; } default: { return super.onTransact(code, data, reply, flags); } } } //The static inner class Proxy of Stub is equivalent to the client private static class Proxy implements IBookManager { // Store the reference of the binder, which is used to call the method of the server private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public String getInterfaceDescriptor() { return DESCRIPTOR; } //Implementation of getBookList() @Override public java.util.List<Book> getBookList() throws android.os.RemoteException { //data is the parameter to be passed. reply stores the return value and result stores the result android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); //Let's start with the operation of cross process communication to see if it can succeed boolean _status = mRemote.transact(IBookManager.Stub.TRANSACTION_getBookList, _data, _reply, 0); //Cross process acquisition failure indicates that the client and server directly call the getbooklist method of stub in one process if (!_status && getDefaultImpl() != null) { return getDefaultImpl().getBookList(); } _reply.readException(); _result = _reply.createTypedArrayList(Book.CREATOR); } finally { //Recycling resources _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(Book book) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((book!=null)) { _data.writeInt(1); book.writeToParcel(_data, 0); } else { _data.writeInt(0); } boolean _status = mRemote.transact(IBookManager.Stub.TRANSACTION_addBook, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { getDefaultImpl().addBook(book); return; } _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } public static IBookManager sDefaultImpl; } //Declare two integer IDs to id entify the custom method static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); public static boolean setDefaultImpl(IBookManager impl) { // Only one user of this interface can use this function // at a time. This is a heuristic to detect if two different // users in the same process use this function. if (Proxy.sDefaultImpl != null) { throw new IllegalStateException("setDefaultImpl() called twice"); } if (impl != null) { Proxy.sDefaultImpl = impl; return true; } return false; } public static IBookManager getDefaultImpl() { return Proxy.sDefaultImpl; } } //Methods declared in IBookManager interface public java.util.List<Book> getBookList() throws android.os.RemoteException; public void addBook(Book book) throws android.os.RemoteException; }
-
-
Workflow
- The client (an Activity) gets the Binder object passed from the server (a Service component) through bindService(), and calls the IBookManager.Stub.asInterface() method to convert it into the IBookManager type object required by the client (if it is not in the same process, the Binder is wrapped with the Proxy class and the object is returned). The method of the server can be called through this object
- The server needs to implement the onbind method and return the binder object. The class to which this binder object belongs must inherit IBookManager Stub class and implement the methods declared in IBookManager. (because stub is an abstract class that implements the IBookManager interface, subclasses must implement the methods declared by the IBookManager interface.)
- When the client calls a method of the server, such as getBookList(), after calling this method, if the client and server are not in the same process, immediately call the corresponding method getBookList() in the Proxy, and then in this method, call the transact() method of the binder to execute the onTransaction() method in the Stub, In this method, you will call the getBookList() method you implemented on the server, then return the results in turn, and the execution ends
be careful:
- The methods in the binder are executed in the binder thread pool, so do not start the thread again in the implementation interface method of the server, unless you are sure that you need to do so.
- If the remote service method is a time-consuming method, the thread needs to be started when the client calls, otherwise ANR will be triggered
Reference books: < < Android development art exploration > >