Android process communication-AIDL use analysis and Binder talk

Article Directory


In the last article, we know the basic use of AIDL for Android process communication-the use of AIDL , but there are still questions about its use, such as:

  1. How does AIDL achieve IPC?
  2. The server creates a Binder object or a client AIDL interface object, and both call the Stub() method to implement it. So what are the specific operations of this method?
  3. What is Binder?

In fact, the above three points can be collectively understood as what the essence of AIDL is; the following focuses on the analysis of the java file generated by the AIDL file. After the AIDL file is created, the SDK will automatically generate the corresponding Binder class, which can be simply understood as AIDL is an implementation Binder tools.
Here are some simple understanding of Binder, concrete can refer Binder interprocess communication mechanisms of analysis , inter-process communication mechanisms Android-Binder

  • From the interface level, Binder is a class in Android, which implements the IBinder interface
  • From the application level, Binder is an intermediate agent for the communication between the client and the server. After bindService, the server will return a proxy object Binder, through which the data of the server can be obtained.
  • From the IPC level, Binder is a cross-process communication method in Android
  • From a bottom-level perspective, Binder is a bridge connecting various Managers (ActivityManager, WindowManager, etc.)
  • From the physical level, Binder is a virtual physical device with its own drive device.

AIDL principle

In the example of the previous article, we created two aidl files, namely IBookManager.aidl and OnNewBookAddListener.aidl files. After rebuild and compile the project, the corresponding java files will be automatically generated, as follows:
Write picture description here
Open the corresponding java file and use IBookManager below .java as an example

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\StudyAndroid\\progress\\src\\main\\aidl\\com\\hzw\\progress\\aidl\\IBookManager.aidl
 */
package com.hzw.progress.aidl;


public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.hzw.progress.aidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.hzw.progress.aidl.IBookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.hzw.progress.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.hzw.progress.aidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.hzw.progress.aidl.IBookManager))) {
                return ((com.hzw.progress.aidl.IBookManager) iin);
            }
            return new com.hzw.progress.aidl.IBookManager.Stub.Proxy(obj);
        }

	 private static class Proxy implements com.hzw.progress.aidl.IBookManager {
 
       *********************省略********************

		}
    }

    public java.util.List<com.hzw.progress.aidl.Book> getBookList() throws android.os.RemoteException;


    public void addBook(com.hzw.progress.aidl.Book book) throws android.os.RemoteException;


    public void registerListener(com.hzw.progress.aidl.OnNewBookAddListener listener) throws android.os.RemoteException;

    public void unregisterListener(com.hzw.progress.aidl.OnNewBookAddListener listener) throws android.os.RemoteException;
}

There are a lot of the above codes, we can roughly know the basic information and content of the java file generated by the AIDL file through the tree structure diagram of the class:
Write picture description here

  • The class IBookManager.java is an interface, inherited from the interface IInterface
  • Then an internal class Stub is declared, and this Stub is inherited from Binder, which is a Binder object; it also implements an instance of IBookManager itself.
  • At the same time, it also declares the four methods defined in the aidl file, namely: getBookList, addBook, registerListener, unregisterListener

It can be seen from the entire code structure that the specific functions of the IBookManager class are implemented in the inner class Stub. Next, let’s look at the logic of the inner class Stub.

public static abstract class Stub extends android.os.Binder implements com.hzw.progress.aidl.IBookManager {

		//Binder的唯一标识,一般是以当前Binder的类名表示
        private static final java.lang.String DESCRIPTOR = "com.hzw.progress.aidl.IBookManager";

	    //将IBookManager接口关联到Binder对象中
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

       //将服务端的Binder对象转换成客户端需要的接口对象IBookManager。
        public static com.hzw.progress.aidl.IBookManager asInterface(android.os.IBinder obj) {
	        //判断obj是否null,为null则说明客户端与服务端连接失败
            if ((obj == null)) {
                return null;
            }
            //通过标识查询obj对象是否有关联的接口
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            //不为null,说明服务端和客户端在同一个进程中,返回自身对象
            if (((iin != null) && (iin instanceof com.hzw.progress.aidl.IBookManager))) {
                return ((com.hzw.progress.aidl.IBookManager) iin);
            }
            //为null,则返回一个Binder的代理对象Proxy
            return new com.hzw.progress.aidl.IBookManager.Stub.Proxy(obj);
        }
… …… ………  ……
}

The Stub class inherits from the Binder class and implements the IBookManager interface at the same time. It defines two own functions, the constructor Stub and asInterface.

  • The IBookManager interface is associated with the Binder object through the constructor Stub(), and the current Binder object is identified by passing in the DESCRIPTOR field to ensure the uniqueness of the Binder object.
  • The Binder object of the server is converted into the interface object IBookManager required by the client through the asInterface function, so as to realize the interaction between the client and the server.

Write picture description here
The asInterface method has the following flow logic

  1. First, it will determine whether the incoming binder object is null. If it is null, it means that the client and server have not established a connection.
  2. Then it will hold the DESCRIPTOR identifier to query whether the binder object has a local corresponding interface (judging by the return value of queryLocalInterface).
  3. Finally, the specific AIDL interface object is returned according to the queryLocalInterface return value. If the return value exists, the server object itself Stub is returned. If it is null, the system encapsulated proxy object Stub.Proxy is returned.

Take a look at the source code of the queryLocalInterface method

 /**
     * Attempt to retrieve a local implementation of an interface
     * for this Binder object.  If null is returned, you will need
     * to instantiate a proxy class to marshall calls through
     * the transact() method.
     */
    public IInterface queryLocalInterface(String descriptor);

From the comments of the source code, we can know a general idea. Through the descriptor identification, it will first try to obtain the local binder object. The so-called local means that the client and the server belong to the same process. If the return value is null, you need to instantiate a proxy object. We can also test the specific returns of queryLocalInterface in different processes

  private ServiceConnection mConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                mIBookManager = IBookManager.Stub.asInterface(service);
                IInterface queryLocalInterface = service.queryLocalInterface("com.hzw.progress.aidl.IBookManager");
                if (queryLocalInterface!=null){
                    //客户端与服务端同一个进程
                    Log.i(TAG, "queryLocalInterface 不为null");
                }else {
                    //不同进程
                    Log.i(TAG, "queryLocalInterface 为null");
                }
}

Since in different processes, asInterface will return a Stub.Proxy proxy object, let’s see how the Proxy object looks like

  private static class Proxy implements com.hzw.progress.aidl.IBookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }


            @Override
            public java.util.List<com.hzw.progress.aidl.Book> getBookList() throws android.os.RemoteException {
	           //获取输入型对象
                android.os.Parcel _data = android.os.Parcel.obtain();
           //获取输出型对象
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.hzw.progress.aidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.hzw.progress.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }


            @Override
            public void addBook(com.hzw.progress.aidl.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);
                    }
                    //执行远程Rcp请求,向服务端发送请求
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
            
            @Override
            public void registerListener(com.hzw.progress.aidl.OnNewBookAddListener listener) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void unregisterListener(com.hzw.progress.aidl.OnNewBookAddListener listener) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

The Proxy proxy class also implements the IBookManager interface and all methods in the IBookManager class. The methods of the proxy class are called on the client, and the client can call the corresponding methods getBookList, addBook, etc. through the proxy object. The approximate internal implementation process is as follows:

  1. Get input Parcel package object _data and output Parcel package object _reply;
  2. If the parameter passed by the method is not null, then write its parameter information into the _data object, which is actually an object serialization process
  3. Then use the remote Binder object to call the transact method to send RPC (remote procedure call) requests, and the current thread will be suspended;
  4. Finally, the server's onTransact will be called. When the server finishes processing the request, it will return the data, and the current thread will continue execution until the _result result is returned. The process of returning data is actually a deserialization process

At the beginning of the third step, when the client sends a remote request to the server and the server receives the request, it will call back the onTransact method of the Binder and return the data to the client. Next, let’s take a look at the implementation logic of onTransact:

		@Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
	        //根据客户端发起请求code,来确定执行
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.hzw.progress.aidl.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
	                //从输入型对象取出参数信息
                    data.enforceInterface(DESCRIPTOR);
                    com.hzw.progress.aidl.Book _arg0;
	                //读取数据
                    if ((0 != data.readInt())) {
	                    //传入参数存在,则反序列化过程
                        _arg0 = com.hzw.progress.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_registerListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.hzw.progress.aidl.OnNewBookAddListener _arg0;
                    _arg0 = com.hzw.progress.aidl.OnNewBookAddListener.Stub.asInterface(data.readStrongBinder());
                    this.registerListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_unregisterListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.hzw.progress.aidl.OnNewBookAddListener _arg0;
                    _arg0 = com.hzw.progress.aidl.OnNewBookAddListener.Stub.asInterface(data.readStrongBinder());
                    this.unregisterListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

When the client initiates a cross-process request, the remote server will process the returned data through a method encapsulated at the bottom of the system. This method is onTransact. Its internal flow is as follows:

  1. For the first time, the client will request the code to determine what needs to be executed.
  2. Then take out the target parameter from the input object data, and if the parameter exists, perform the deserialization operation.
  3. When the target method is executed, data is written to the output reply object, and the data is returned to the Binder object, and finally to the client.

The above is the principle flow of the entire AIDL implementation process communication.

to sum up

The following briefly summarizes the working mechanism of Binder

Write picture description here

Binder's workflow can be divided into three parts, namely connection request, serialized write data, deserialization results returned to the client
1. After the binding service establishes a connection, the server will return a Binder object to the client through the Binder Objects can interact across processes. When obtaining a Binder instance object, you need to call the asInterface method to determine whether the client and server are in the same process. If they are in the same process, a local binder object is returned. Otherwise, a proxy object Stub.Proxy needs to be created to return.
2. After obtaining the proxy object Proxy, the client can call the corresponding methods of the server (addBook, getBookList, etc.) to pass in the parameters. When the Binder gets the data, it will write the package class according to the presence or absence of the parameters (serialization process) In Parcel, then call the transact method of the binder object.
3. Finally, the onTransact method of the Stub class (Binder class) will be triggered, and the data will be taken from the thread pool, written to the reply, and the result will be returned to the client, and finally the thread will be awakened.

reference

  • Android development art exploration
  • https://www.jianshu.com/p/b96713fc4e5e
  • https://blog.csdn.net/u011240877/article/details/72801425

Guess you like

Origin blog.csdn.net/hzw2017/article/details/81056445