Comunicación de procesos de Android: análisis de uso de AIDL y charla de Binder

Directorio de artículos


En el último artículo, conocemos el uso básico de AIDL para la comunicación de procesos de Android, el uso de AIDL , pero aún existen dudas sobre su uso, tales como:

  1. ¿Cómo logra AIDL el IPC?
  2. El servidor crea un objeto Binder o un objeto de interfaz AIDL de cliente, y ambos llaman al método Stub () para implementarlo. Entonces, ¿cuáles son las operaciones específicas de este método?
  3. ¿Qué es Binder?

De hecho, los tres puntos anteriores pueden entenderse colectivamente como cuál es la esencia de AIDL; lo siguiente se centra en el análisis del archivo java generado por el archivo AIDL. Una vez creado el archivo AIDL, el SDK generará automáticamente el Binder correspondiente. class, que puede entenderse simplemente como AIDL es una implementación de herramientas Binder.
Éstos son algunos sencilla comprensión de Binder, el hormigón puede referirse Binder entre procesos mecanismos de comunicación de análisis , los mecanismos de comunicación entre procesos de Android-Binder

  • Desde el nivel de la interfaz, Binder es una clase en Android, que implementa la interfaz IBinder
  • Desde el nivel de la aplicación, Binder es un agente intermedio para la comunicación entre el cliente y el servidor, luego de bindService, el servidor devolverá un objeto proxy Binder, a través del cual se pueden obtener los datos del servidor.
  • Desde el nivel de IPC, Binder es un método de comunicación entre procesos en Android
  • Desde una perspectiva de nivel inferior, Binder es un puente que conecta a varios administradores (ActivityManager, WindowManager, etc.)
  • Desde el nivel físico, Binder es un dispositivo físico virtual con su propio dispositivo de disco.

Principio AIDL

En el ejemplo del artículo anterior, creamos dos archivos aidl, a saber, los archivos IBookManager.aidl y OnNewBookAddListener.aidl. Después de reconstruir y compilar el proyecto, los archivos Java correspondientes se generarán automáticamente, de la siguiente manera:
Escriba la descripción de la imagen aquí
Abra el archivo Java correspondiente y use IBookManager debajo de .java como ejemplo

/*
 * 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;
}

Hay muchos de los códigos anteriores, podemos conocer aproximadamente la información básica y el contenido del archivo java generado por el archivo AIDL a través del diagrama de estructura de árbol de la clase:
Escriba la descripción de la imagen aquí

  • La clase IBookManager.java es una interfaz, heredada de la interfaz IInterface
  • Luego, se declara un Stub de clase interna, y este Stub se hereda de Binder, que es un objeto Binder; también implementa una instancia del propio IBookManager.
  • Al mismo tiempo, también declara los cuatro métodos definidos en el archivo aidl, a saber: getBookList, addBook, registerListener, unregisterListener

Se puede ver en toda la estructura del código que las funciones específicas de la clase IBookManager se implementan en la clase interna Stub. A continuación, veamos la lógica de la clase interna 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);
        }
… …… ………  ……
}

La clase Stub hereda de la clase Binder y al mismo tiempo implementa la interfaz IBookManager, define dos funciones propias, el constructor Stub y asInterface.

  • La interfaz IBookManager se asocia con el objeto Binder a través del constructor Stub (), y el objeto Binder actual se identifica pasando el campo DESCRIPTOR para garantizar la unicidad del objeto Binder.
  • El objeto Binder del servidor se convierte en el objeto de interfaz IBookManager requerido por el cliente a través de la función asInterface, para realizar la interacción entre el cliente y el servidor.

Escriba la descripción de la imagen aquí
El método asInterface tiene la siguiente lógica de flujo

  1. Primero, determinará si el objeto de enlace entrante es nulo, si es nulo, significa que el cliente y el servidor no han establecido una conexión.
  2. Luego, contendrá el identificador DESCRIPTOR para consultar si el objeto de enlace tiene una interfaz local correspondiente (a juzgar por el valor de retorno de queryLocalInterface).
  3. Finalmente, el objeto de interfaz AIDL específico se devuelve de acuerdo con el valor devuelto queryLocalInterface. Si el valor devuelto existe, se devuelve el objeto servidor Stub. Si es nulo, se devuelve el objeto proxy encapsulado del sistema Stub.Proxy.

Eche un vistazo al código fuente del método queryLocalInterface

 /**
     * 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);

A partir de los comentarios del código fuente podemos conocer una idea general, a través de la identificación del descriptor, primero se intentará obtener el objeto binder local, el llamado local significa que el cliente y el servidor pertenecen al mismo proceso. Si el valor de retorno es nulo, debe crear una instancia de un objeto proxy. También podemos probar los retornos específicos de queryLocalInterface en diferentes procesos

  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");
                }
}

Dado que en diferentes procesos, asInterface devolverá un objeto proxy Stub.Proxy, veamos cómo se ve el objeto Proxy

  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();
                }
            }
        }

La clase de proxy Proxy también implementa la interfaz IBookManager y todos los métodos en la clase IBookManager. Los métodos de la clase de proxy se llaman en el cliente, y el cliente puede llamar a los métodos correspondientes getBookList, addBook, etc. a través del objeto proxy. El proceso de implementación interno aproximado es el siguiente:

  1. Obtiene el objeto de paquete Parcel de entrada _data y el objeto de paquete Parcel de salida _reply;
  2. Si el parámetro pasado por el método no es nulo, escriba su información de parámetro en el objeto _data, que en realidad es un proceso de serialización de objetos.
  3. Luego use el objeto Binder remoto para llamar al método transact para enviar solicitudes RPC (llamada a procedimiento remoto), y el hilo actual se suspenderá;
  4. Finalmente, se llamará al onTransact del servidor. Cuando el servidor termine de procesar la solicitud, devolverá los datos y el hilo actual continuará ejecutándose hasta que se devuelva el resultado _result. El proceso de devolución de datos es en realidad un proceso de deserialización.

Al comienzo del tercer paso, cuando el cliente envía una solicitud remota al servidor y el servidor recibe la solicitud, volverá a llamar al método onTransact del Binder y devolverá los datos al cliente. A continuación, echemos un vistazo a la lógica de implementación de 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);
        }

Cuando el cliente inicia una solicitud de proceso cruzado, el servidor remoto procesará los datos devueltos a través de un método encapsulado en la parte inferior del sistema. Este método es onTransact. Su flujo interno es el siguiente:

  1. Por primera vez, el cliente solicitará el código para determinar qué debe ejecutarse.
  2. Luego saque el parámetro de destino de los datos del objeto de entrada y, si el parámetro existe, realice la operación de deserialización.
  3. Cuando se ejecuta el método de destino, los datos se escriben en el objeto de respuesta de salida y los datos se devuelven al objeto Binder y, finalmente, al cliente.

Lo anterior es el flujo principal de toda la comunicación del proceso de implementación de AIDL.

para resumir

A continuación se resume brevemente el mecanismo de trabajo de Binder.

Escriba la descripción de la imagen aquí

El flujo de trabajo de Binder se puede dividir en tres partes, a saber, solicitud de conexión, datos de escritura serializados, resultados de deserialización devueltos al cliente
1. Una vez que el servicio de enlace establece una conexión, el servidor devolverá un objeto Binder al cliente a través de Binder Objects puede interactuar a través de Procesos. Al obtener el objeto de instancia de Binder, debe llamar al método asInterface para determinar si el cliente y el servidor están en el mismo proceso. Si están en el mismo proceso, se devuelve un objeto de enlace local. De lo contrario, un objeto de proxy Stub. Es necesario crear un proxy para regresar.
2. Después de obtener el objeto proxy Proxy, el cliente puede llamar a los métodos correspondientes del servidor (addBook, getBookList, etc.) para pasar los parámetros. Cuando el Binder obtenga los datos, escribirá la clase del paquete de acuerdo con la presencia o ausencia de los parámetros (proceso de serialización) En Parcel, luego llame al método transact del objeto binder.
3. Finalmente, se activará el método onTransact de la clase Stub (clase Binder) y los datos se tomarán del grupo de subprocesos, se escribirán en la respuesta y el resultado se devolverá al cliente, y finalmente el subproceso ser despertado.

referencia

  • Exploración de arte de desarrollo de Android
  • https://www.jianshu.com/p/b96713fc4e5e
  • https://blog.csdn.net/u011240877/article/details/72801425

Supongo que te gusta

Origin blog.csdn.net/hzw2017/article/details/81056445
Recomendado
Clasificación