Android multi-process series

  • Several basic problems of Android multi-process communication
  • Binder for Android multiprocess
  • Android multiprocess Binder unbinding listener issues
  • Accidental death and permissions verification for Android Multiprocess Binder
  • Android Multiprocess Messenger

Analyze system generated Binder classes

package com.xxq2dream.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.xxq2dream.aidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.xxq2dream.aidl.IBookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() { this.attachInterface(this, DESCRIPTOR); } / * * * used to convert Binder object from the server to the client need an AIDL interface object of type * / public static com. Xxq2dream. AIDL. IBookManager asInterface(android.os.IBinder obj) {if ((obj == null)) {
                returnnull; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); If the client and server are in the same process, the Stub object on the server is returned, and the transact method across the process is not invokedif(((iin ! = null) && (iin instanceof com.xxq2dream.aidl.IBookManager))) {return((com.xxq2dream.aidl.IBookManager) iin); } // If the client and server are not in the same process, the return is a system wrapped stub. Proxy Proxy object, and the client invocation uses the Transact method to initiate a cross-process requestreturnnew com.xxq2dream.aidl.IBookManager.Stub.Proxy(obj); } // Return the current Binder object @override public android.os.ibinderasBinder() {
            returnthis; } /** * run in the Binder thread pool on the server side ** @param code method identifier to run * @param data parameters passed by the client * @param Reply data returned to the client * @param flags * @return trueThe command is executed successfully.false, execution failed, * @throws RemoteException */ @Override public Boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) {case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                caseTRANSACTION_getBookList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.xxq2dream.aidl.Book> _result = this.getBookList(); reply.writeNoException(); // Write the result to reply and return reply.writetypedList (_result);return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.xxq2dream.aidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.xxq2dream.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true; }}return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.xxq2dream.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() {
                returnDESCRIPTOR; } /** * Run in the client's Binder thread pool * When the client and the server are not in the same process, the asInterface method above returns this Proxy object * To call the server is to call the corresponding method here. Finally, the transact method initiates a remote procedure call * * @return* @throws RemoteException */ @Override public java.util.List<com.xxq2dream.aidl.Book> getBookList() throws Android. OS. RemoteException {/ / to the service side pass parameters. Android OS. The Parcel _data while forming. = android OS. Parcel. Obtain (); Android.os.parcel _reply = Android.os.parcel. Obtain (); android.os.parcel. java.util.List<com.xxq2dream.aidl.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); Mremote. transact(stub.transaction_getbooklist, _data, _reply, 0); // After the above call is returned, the current thread continues to execute, retrieving the result from reply and returning _reply.readexception (); _result = _reply.createTypedArrayList(com.xxq2dream.aidl.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); }return _result;
            }

            @Override
            public void addBook(com.xxq2dream.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); } mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); Static final int TRANSACTION_getBookList = (Android.os.ibinder.first_call_transaction + 0); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } / / interfaces provide public Java. Util. List < com. Xxq2dream. Aidl. Book > getBookList () throws. Android OS. Remoteexceptions. public void addBook(com.xxq2dream.aidl.Book book) throws android.os.RemoteException; }Copy the code

Write Binder classes by hand

  • According to the above analysis, Binder class can be divided into two parts. One is Stub class, which is the real Binder class. Another is the AIDL interface
Extract the AIDL interface
public interface IBookManager extends IInterface {

    static final String DESCRIPTOR = "com.xxq2dream.manualbinder.IBookManager"; static final int TRANSACTION_getBookList = IBinder.FIRST_CALL_TRANSACTION + 0; static final int TRANSACTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1; <! -- Comment 1--> <! --static final int TRANSACTION_registerListener= IBinder.FIRST_CALL_TRANSACTION + 2; -- > <! --static final int TRANSACTION_unRegisterListener= IBinder.FIRST_CALL_TRANSACTION + 3; --> public List<Book> getBookList() throws RemoteException; public void addBook(Book book) throws RemoteException; <! -- Note 2--> <! --void registerListener(IOnNewBookArrivedListener listener) throws RemoteException; -- > <! --void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException; -- >}Copy the code
A proxy class that implements Stub and Stub classes
  • This is mostly the same as the auto-generated code above
public class BookManagerImpl extends Binder implements IBookManager {
    private static final String TAG = "BookManagerImpl";

    public BookManagerImpl() {
        Log.e(TAG, "attachInterface-->"+ System.currentTimeMillis());
        this.attachInterface(this,DESCRIPTOR);
    }

    /**
     * Cast an IBinder object into an com.xxq2dream.IBookManager interface,
     * generating a proxy if needed.
     */
    public static IBookManager asInterface(IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if(((iin ! = null) && (iin instanceof IBookManager))) { Log.e(TAG,"asInterface");
            return ((IBookManager) iin);
        }
        Log.e(TAG, "asInterface Proxy-->"+ System.currentTimeMillis());
        returnnew BookManagerImpl.Proxy(obj); } /** * run in the Binder thread pool on the server side ** @param code method identifier to run * @param data parameters passed by the client * @param Reply data returned to the client * @param flags * @return trueThe command is executed successfully.false, execution failed, * @throws RemoteException */ @Override public Boolean onTransact(int code, Parcel data, Parcel Reply, int flags) throws RemoteException { Log.e(TAG,"onTransact-->"+System.currentTimeMillis());

        switch (code) {
            case INTERFACE_TRANSACTION: {
                Log.e(TAG, "INTERFACE_TRANSACTION-->"+ System.currentTimeMillis());
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_getBookList: {
                Log.e(TAG, "TRANSACTION_getBookList-->"+ System.currentTimeMillis());

                data.enforceInterface(DESCRIPTOR);
                List<Book> result = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(result);
                return true;
            }
            case TRANSACTION_addBook: {
                Log.e(TAG, "TRANSACTION_addBook-->"+ System.currentTimeMillis());

                data.enforceInterface(DESCRIPTOR);
                Book arg0;
                if (0 != data.readInt()) {
                    arg0 = Book.CREATOR.createFromParcel(data);
                }else {
                    arg0 = null;
                }
                this.addBook(arg0);
                reply.writeNoException();
                return true; } <! -- Note 6--> <! --case TRANSACTION_registerListener: {--> <! -- Log.e(TAG,"TRANSACTION_registerListener-->"+System.currentTimeMillis()); -- > <! -- data.enforceInterface(DESCRIPTOR); -- > <! -- IOnNewBookArrivedListener arg0; -- > <! -- arg0 = OnNewBookArrivedListenerImpl.asInterface(data.readStrongBinder()); -- > <! -- this.registerListener(arg0); -- > <! -- reply.writeNoException(); -- > <! --return true; -- > <! -} -- > <! --case TRANSACTION_unRegisterListener: {--> <! -- Log.e(TAG,"TRANSACTION_unRegisterListener-->"+System.currentTimeMillis()); -- > <! -- data.enforceInterface(DESCRIPTOR); -- > <! -- IOnNewBookArrivedListener arg0; -- > <! -- arg0 = OnNewBookArrivedListenerImpl.asInterface(data.readStrongBinder()); -- > <! -- this.unRegisterListener(arg0); -- > <! -- reply.writeNoException(); -- > <! --return true; -- > <! -} -- >}return super.onTransact(code, data, reply, flags);
    }

    @Override
    public List<Book> getBookList() throws RemoteException {
        returnnull; } @Override public void addBook(Book book) throws RemoteException { } <! -- Comment 5--> <! --@Override--> <! --public void registerListener(IOnNewBookArrivedListener listener) {--> <! -} -- > <! --@Override--> <! --public void unRegisterListener(IOnNewBookArrivedListener listener) {--> <! --}--> /** * Retrieve the Binder object associated with this interface. * You must use this instead of a plain cast, so that proxy objects * canreturn the correct result.
     */
    @Override
    public IBinder asBinder() {
        Log.e(TAG, "asBinder-->"+ System.currentTimeMillis());
        return this;
    }

    private static class Proxy implements IBookManager {

        private IBinder mRemote;

        Proxy(IBinder remote) {
            mRemote = remote;
        }

        public String getInterfaceDescriptor() {
            returnDESCRIPTOR; } /** * runs in the client's Binder thread pool ** @return
         * @throws RemoteException
         */
        @Override
        public List<Book> getBookList() throws RemoteException {
            Log.e(TAG, "Proxy-->getBookList-->"+ System.currentTimeMillis()); Parcel data = Parcel. Obtain (); // Parcel data = Parcel. Parcel reply = Parcel. Obtain (); List<Book> result; try { data.writeInterfaceToken(DESCRIPTOR); Mremote. transact(TRANSACTION_getBookList, data, reply, 0); ReadException (); // After the above call is returned, the current thread continues to execute, retrieving the result from reply and returning reply.readexception (); result = reply.createTypedArrayList(Book.CREATOR); } finally { reply.recycle(); data.recycle(); }return result;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            Log.e(TAG, "Proxy-->addBook-->"+ System.currentTimeMillis());

            //用于向服务端传递参数
            Parcel data = Parcel.obtain();
            //用于接收服务端传回来的结果
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                if(book ! = null) { data.writeInt(1); book.writeToParcel(data,0); }else{ data.writeInt(0); } mRemote.transact(TRANSACTION_addBook, data, reply, 0); reply.readException(); }finally { reply.recycle(); data.recycle(); }} <! -- Note 3--> <! --@Override--> <! --public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException{--> <! -- Log.e(TAG,"Proxy-->registerListener-->"+ System.currentTimeMillis()); -- > <! -- // Used to pass parameters to the server --> <! -- Parcel data = Parcel.obtain(); -- > <! -- // To receive the result from the server --> <! -- Parcel reply = Parcel.obtain(); -- > <! -- try {--> <! -- data.writeInterfaceToken(DESCRIPTOR); -- > <! -- data.writeStrongBinder((listener ! = null)? (listener.asBinder()) : (null)); -- > <! -- mRemote.transact(TRANSACTION_registerListener, data, reply, 0); -- > <! -- reply.readException(); -- > <! -- }finally {--> <! -- reply.recycle(); -- > <! -- data.recycle(); -- > <! -} -- > <! -} -- > <! -- Note 4--> <! --@Override--> <! --public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException{--> <! -- Log.e(TAG,"Proxy-->unRegisterListener-->"+ System.currentTimeMillis()); -- > <! -- // Used to pass parameters to the server --> <! -- Parcel data = Parcel.obtain(); -- > <! -- // To receive the result from the server --> <! -- Parcel reply = Parcel.obtain(); -- > <! -- try {--> <! -- data.writeInterfaceToken(DESCRIPTOR); -- > <! -- data.writeStrongBinder((listener ! = null)? (listener.asBinder()) : (null)); -- > <! -- mRemote.transact(TRANSACTION_unRegisterListener, data, reply, 0); -- > <! -- reply.readException(); -- > <! -- }finally {--> <! -- reply.recycle(); -- > <! -- data.recycle(); -- > <! -} -- > <! --}--> /** * Retrieve the Binder object associated with this interface. * You must use this instead of a plain cast, so that proxy objects * canreturn the correct result.
         */
        @Override
        public IBinder asBinder() {
            Log.e(TAG, "Proxy-->asBinder-->"+ System.currentTimeMillis());
            returnmRemote; }}}Copy the code
  • Binder classes are our own, with no systematic approach and no AIDL files
Extend our Binder class
  • For example, we want to implement the observer mode, which notifies the client when the server has a new book. We need to bind the listening interface to the client after binding the server, and also provide the unbound interface. Comments 1-6 marked in the code above are the steps to extend the Binder class
  • Of course, since AIDL does not support normal interfaces, only AIDL interfaces are supported, we need to add the AIDL interface service on the server side, as in the above code must be the interface implemented by AIDL, i.e
  • So we also have to add an AIDL interface, and again we can either write an AIDL file that the system automatically generates, or we can write it manually. Again, we’re going to write it manually
  • AIDL interface IOnNewBookArrivedListener
public interface IOnNewBookArrivedListener extends IInterface {
    static final String DESCRIPTOR = "com.xxq2dream.aidl.IOnNewBookArrivedListener";

    static final int TRANSACTION_onNewBookArrived = IBinder.FIRST_CALL_TRANSACTION + 0;

    void onNewBookArrived(Book book) throws RemoteException;
}
Copy the code
  • Corresponding Binder class
public class OnNewBookArrivedListenerImpl extends Binder implements IOnNewBookArrivedListener {
    private static final String TAG = "NewBookArrivedListener";

    public OnNewBookArrivedListenerImpl() {
        Log.e(TAG, "attachInterface-->"+ System.currentTimeMillis());
        this.attachInterface(this,DESCRIPTOR);
    }

    public static IOnNewBookArrivedListener asInterface (IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if(((iin ! = null) && (iin instanceof IOnNewBookArrivedListener))) { Log.e(TAG,"asInterface");
            return ((IOnNewBookArrivedListener) iin);
        }
        Log.e(TAG, "asInterface Proxy-->"+ System.currentTimeMillis());
        returnnew OnNewBookArrivedListenerImpl.Proxy(obj); } /** * run in the Binder thread pool on the server side ** @param code method identifier to run * @param data parameters passed by the client * @param Reply data returned to the client * @param flags * @return trueThe command is executed successfully.false, execution failed, * @throws RemoteException */ @Override public Boolean onTransact(int code, Parcel data, Parcel Reply, int flags) throws RemoteException { Log.e(TAG,"onTransact-->"+System.currentTimeMillis());

        switch (code) {
            case INTERFACE_TRANSACTION: {
                Log.e(TAG, "INTERFACE_TRANSACTION-->"+ System.currentTimeMillis());
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_onNewBookArrived: {
                Log.e(TAG, "TRANSACTION_onNewBookArrived-->"+ System.currentTimeMillis());

                data.enforceInterface(DESCRIPTOR);
                Book _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = Book.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                this.onNewBookArrived(_arg0);
                reply.writeNoException();
                return true; }}returnsuper.onTransact(code, data, reply, flags); } @Override public void onNewBookArrived(Book book) { } /** * Retrieve the Binder object associated with this interface.  * You must use this instead of a plain cast, so that proxy objects * canreturn the correct result.
     */
    @Override
    public IBinder asBinder() {
        return this;
    }

    private static class Proxy implements IOnNewBookArrivedListener {

        private IBinder mRemote;

        Proxy(IBinder remote) {
            mRemote = remote;
        }

        public String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

        /**
         * Retrieve the Binder object associated with this interface.
         * You must use this instead of a plain cast, so that proxy objects
         * can return the correct result.
         */
        @Override
        public IBinder asBinder() {
            Log.e(TAG, "Proxy-->asBinder-->"+ System.currentTimeMillis());
            return mRemote;
        }

        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException{
            Log.e(TAG, "onNewBookArrived-->" + System.currentTimeMillis());
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                if((newBook ! = null)) { _data.writeInt(1); newBook.writeToParcel(_data, 0); }else{ _data.writeInt(0); } mRemote.transact(TRANSACTION_onNewBookArrived, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); }}}}Copy the code
Binder delivers the AIDL interface
  • As you can see from the code above, instead of passing a normal Parcelable serialized object, the writeStrongBinder method is used to add the AIDL interface to the Proxy parameters
  • The interface parameters are passed through the asInterface method of the Binder class in the onTransact method on the server side
IOnNewBookArrivedListener arg0;
arg0 = OnNewBookArrivedListenerImpl.asInterface(data.readStrongBinder());
Copy the code
conclusion
  • By writing Binder classes by hand, we can better understand how Binder works
  • After extending the Binder class, the next step is to retrofit the client and server to add the ability to register listeners

Welcome to follow my wechat official number, and learn and grow together with me!Copy the code