preface

IPC stands for inter-process Communication.Copy the code

Binder is an in-depth topic. This article will not go into the bottom details of Binder, but will focus on its use and upper principles.

Binder is a class in Android that implements the IBinder interface. Binder is a cross-process communication method in Android. Binder can also be understood as a virtual physical device, which is driven by /dev/binder. At the Framework level, a Binder is a bridge between a ServiceManager’s various managers and the corresponding ManagerService. At the Android application layer, Binder is the communication medium between client and server. BinderService is used to bind remote services for communication.

Data types supported in AIDL

  • Basic data types (int, long, char, Boolean, double, etc.)
  • String and CharSequence
  • List: Only ArrayList is supported, and every element in it must be able to be supported by AIDL
  • Map: Only HashMap is supported, and every element in it must be supported by AIDL, including keys.
  • Parcelable: All objects that implement the Parcelable interface.
  • MAIDL: All AIDL interfaces themselves can also be used in AIDL.

AIDL generates the structure diagram and inserts the picture description here

Let’s implement a simple AIDL and examine its communication process

Intent intent = new Intent(this, MyService.class);
bindService(intent,connection,BIND_AUTO_CREATE);
Copy the code

When we call the bindService method. The system will wake up the corresponding remote server for us, see the source code know is through the ServiceManager to remote service with ClassLoader reflection to start the need to start the service. Details we see the frameWork layer source code, too many things, here only analysis of AIDL application layer process.

The BookBind class is created as soon as the Service starts

@Override public IBinder onBind(Intent intent) { return new BookBind(); } private class BookBind extends IBookManager.Stub { @Override public List<Book> getBookList() throws RemoteException { return books; } @Override public void addBook(Book book) throws RemoteException { books.add(book); Log.d("lichao"," service addBook " + book.bookName); }}Copy the code

This class is going to go again

AIDLDemo\app\build\generated\source\aidl\debug\com\love\candy\aidl\IBookManager.java
Copy the code
Public Stub() {// This stands for IBookManager, registers the IInterface this.attachInterface(this, DESCRIPTOR); } public void attachInterface(IInterface owner, String descriptor) { mOwner = owner; mDescriptor = descriptor; }Copy the code

So here we see that we have an IInterface coming in, and IBookManager inherits from IInterface.

The server returns a Binder to the client

/** ** Bind */ ServiceConnection connection = new ServiceConnection() {@override public void onServiceConnected(ComponentName name, IBinder service) { iBookManager = IBookManager.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { iBookManager=null; }};Copy the code

We want to call IBookManager. Stub. AsInterface (service), at that time will come again

/ / convert an IBinder object into a com. Love. Candy. Aidl, here is the method that we usually use public static com. Love. Candy. Aidl. IBookManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } // We use queryLocalInterface to see if there is an IInterface, which is already in when we call the Stub constructor. So this android.os.IInterface iin = obj. QueryLocalInterface (DESCRIPTOR); If (((iin! = null) && (iin instanceof com.love.candy.aidl.IBookManager))) { return ((com.love.candy.aidl.IBookManager) iin); } / / or create a new Proxy return new com. Love. Candy. Aidl. IBookManager. Stub. The Proxy (obj); }Copy the code

When we call the method that accesses the remote service iBookManager.addBook(book); It calls the addBook method in the Proxy.

@ Override public void same (com. Love. Candy. Aidl. Book Book) throws. Android OS. RemoteException {/ / to get serialized objects android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); Try {// Write the aiDL to a place via NDK _data.writeInterfaceToken(DESCRIPTOR); // If the value passed is not null if ((book! = null)) {// Serialize the incoming value _data.writeint (1); book.writeToParcel(_data, 0); } else { _data.writeInt(0); } // Bind's transact method mremote. transact(stub.transaction_addbook, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); }}Copy the code

By encapsulating the object a bit, he ends up calling the mremote.transact method.

public final boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { if (false) Log.v("Binder", "Transact: " + code + " to " + this); if (data ! = null) { data.setDataPosition(0); } // This is called back to our Stub by Binder's onTransact method Boolean r = onTransact(code, data, reply, flags); if (reply ! = null) { reply.setDataPosition(0); } return r; }Copy the code

This will call back to onTransact in our Stub.

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) { switch (code) { case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.love.candy.aidl.Book _arg0; // The Proxy value corresponds to if ((0! = data. ReadInt ())) {/ / create a Book through the data object _arg0 = com. Love. Candy. The aidl. Book. The CREATOR. CreateFromParcel (data); } else { _arg0 = null; } // Call the abstract method this.addbook (_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); }Copy the code

This parses the object we passed and calls back to Stub’s abstract method addBook, which is the same method we overwrote in Service

private class BookBind extends IBookManager.Stub { @Override public List<Book> getBookList() throws RemoteException { return books; } @Override public void addBook(Book book) throws RemoteException { books.add(book); Log.d("lichao"," service addBook " + book.bookName); }}Copy the code

Finally, the addBook method in the server is called, and the book added by the client is added to the server list. Fetching remote service data is similar to joining a data process, except fetching data has a return value.

The flow chart is shown below

Baidu daishen was quoted.

summary

When our client calls a remote service method, run by the method called Binder on the service end thread pool, at the same time, the client threads will be hung, at this time if the server-side method performs more time-consuming, can lead to where the client threads blocked for a long time, and if the client is the UI thread, can cause ANR, Therefore, we should avoid accessing remote methods in the UI thread of the client. Because the onServiceConnected and onServiceDisconnected methods on the client side both run in the UI thread, there are no time-consuming operations to be done here either. In addition, because server-side methods run in the server’s binder thread pool, server-side methods can perform a large number of time-consuming operations themselves. It is important not to create new threads in server-side methods to perform asynchronous tasks.

Making the demo address

BinderService startup process analysis

The last

Android learning is a long road, we have to learn things not only surface technology, but also go deep into the bottom, understand the principle below, only in this way, we can improve their competitiveness, in today’s competitive world.

As the saying goes, there are two best times to plant a tree, one is ten years ago and the other is now.

A journey of a thousand miles begins with a single step. May you and I encourage each other.