Análisis del mecanismo de comunicación Binder en el sistema Android (7) - Mecanismo Binder de la capa Java

declaración

1 marco Binder de la capa Java

  La biblioteca del marco de trabajo de Java proporciona un conjunto de clases relacionadas con Binder, de modo que el código de Java pueda usar Binder directamente para compilar IPC. Las clases principales están en el directorio core/java/android/os/ y son parte del paquete android.os. Además, el directorio core/java/com/android/internal/os/ proporciona la clase BinderIntermal en el paquete com.android.internal.os, que es una clase de soporte de Binder.
  El contenido admitido por Java Layer Binder proviene del local y se encapsula en la capa Java a través de JNI. El Binder de la capa Java y el Binder de la capa local se implementan sobre la misma base.

1.1 Clases relacionadas con el mecanismo Binder

  Las API principales de Binder están en el paquete android.os, son: interfaz IInterface, interfaz IBinder, clase Binder y clase Parcel.
lInterface es una interfaz a la que los implementadores proporcionan métodos reales. que solo tiene el método:

             static sp<IBinder>  asBinder(const IInterface*);
             static sp<IBinder>  asBinder(const sp<IInterface>&);

  El método asBinder() se usa para convertir una IInterface en un tipo IBinder. IBinder también es una interfaz utilizada para actuar como identificador de comunicación entre los dos extremos de la IPC, cuyo contenido principal es el método transact(), como se muestra a continuación:

    virtual status_t        transact(   uint32_t code,
                                        const Parcel& data,
                                        Parcel* reply,
                                        uint32_t flags = 0) = 0;

  transact() se utiliza para comunicarse con un Binder remoto, que contiene 4 parámetros de código de comunicación de tipo entero, datos de parámetros de tipo Paquete y respuesta de valor de retorno, y banderas de tipo entero. Varios otros métodos importantes en IBinder son los siguientes:

	virtual const String16& getInterfaceDescriptor() const = 0;
    virtual sp<IInterface>  queryLocalInterface(const String16& descriptor);
    virtual status_t        pingBinder() = 0;

  getInterfaceDescriptor() se usa para obtener la interfaz correspondiente a este Binder, y queryLocalInterface() se usa para obtener el identificador de implementación de la IInterface correspondiente a este objeto Binder. pingBinder() se usa para consultar si el Binder aún existe. Binder es una clase que implementa la interfaz IBinder, y esta clase sigue siendo una clase abstracta. La razón principal es que el método onTransact() en la clase Binder no está implementado, como se muestra a continuación:

protected:
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);

  IBinder.transact() y Binder.onTransact() tienen una relación correspondiente. El método llamado en IBinder.transact() regresará desde Binder.onTransact(), y este es un método de llamada síncrono.
En el programa, el implementador de la interfaz IInterface se puede usar principalmente para representar una interfaz específica; no heredará directamente la interfaz IBinder, pero heredará la clase abstracta Binder.

  Parcel es una clase utilizada para encapsular datos transmitidos. El contenido admitido en la clase Parcel es el tipo de parámetros y valores de retorno que se pueden usar al usar Binder para comunicarse entre procesos. Parcel admite tipos básicos, tipos de cadenas, tipos de objetos Binder, tipos Parcelable y varios tipos de matrices. Para obtener detalles sobre Parcel, consulte: Análisis del mecanismo de comunicación de Binder en el sistema Android (5) - Paquete de portador de transferencia de datos entre procesos
  Para un implementador de IInterface, puede llamar a asBinder(), convertirlo en tipo IBinder y pasarlo Paquete arriba. De hecho, un nombre llamado writeStrongInterface() en Parcel usa este método y lo realiza llamando a writeStrongBinder() .

1.2 archivo de descripción aidl

  El nombre completo de AIDL es Lenguaje de definición de interfaz de Android (Lenguaje de descripción de interfaz de Android). El papel de IDL es utilizar una sintaxis especial para describir la interfaz, que es una colección de métodos (funciones).
  La herramienta aidl en Android puede generar automáticamente un archivo de código Java de acuerdo con el archivo aidl, y el contenido es un marco para usar específicamente Binder para IPC .
El proceso básico de construcción de un marco de comunicación específico utilizando el mecanismo Binder en la capa de Java es:

  1. Usa el archivo aidl para declarar la interfaz IXXX y compilarla;
  2. En la sección de servicio, la función real se realiza al heredar IXXX.Stub;
  3. En el lado del cliente, use IXXXStub.asInterface() para obtener la interfaz de tipo IXXX y llamarla.

  En el programa, la implementación específica necesita definir la interfaz IXXX.aidl. Después de agregar IXXX.aidl a la compilación, la herramienta aidl en Android lo procesará y se generará automáticamente un archivo llamado XXX.java. Incluye la interfaz IXXX y la clase abstracta IXXXStub, que son el marco de la estructura Binder específica.
  Debido al mecanismo de generación automática, el marco de Binder específico solo necesita escribir un archivo aidl relativamente simple y no necesita escribir los detalles del código para la comunicación a través de Binder IPC. Para el desarrollador del programa, hay tres cosas que deben hacerse: el archivo aidl, el servidor hereda IXXX.Stub y el cliente recibe la llamada de tipo IXXX a través de IXX.Stub.asInterface() . El IXXX.Stub generado automáticamente es un heredero de la clase Binder.

La combinación de Binder y aidl se muestra en la siguiente figura:
inserte la descripción de la imagen aquí
  representa el formato de sintaxis en el archivo aidl y la definición de una interfaz Java. Pero lo que representa no es una interfaz simple, sino que tiene mucho contenido adicional. Por ejemplo, un fragmento de un archivo llamado IAidITest.aidl se ve así:

package android.os;				//接口所属于的包
import android.os.AidlTest;		//需要导入的类
interface IAidlTest {
    
    
//各个方法的集合
	int intMethod(int a);
	boolean[] booleanArray(in boolean[] a0, out boolean[] al, inout boolean[] a2);
	char[] charArray(in char[] a0, out char[] al, inout char[] a2);
	String[] stringArray(in String[] a0, out String[] al, inout Stringl] a2);
	void voidSecurityException();
	int intSecurityException();
//省略部分内容
}

  interface es una palabra clave en el archivo aidl, que se usa para definir el nombre de la interfaz, y el contenido principal es una colección de varios métodos. La sintaxis en aidl es básicamente la misma que la definición de interfaz en Java, excepto que los tipos de parámetros y los valores devueltos son limitados. in, out e inout indican la dirección del paso de parámetros.
  Hay otra palabra clave unidireccional en el archivo aidl, que se usa delante de la interfaz. Significa que la persona que llama no esperará el resultado de devolución del método en la interfaz, por lo que la clase de devolución del método en la interfaz modificada con oneway es nula. Oneway se usa a menudo para representar interfaces que "devuelven la llamada" a la funcionalidad.
  En el entorno de desarrollo del código fuente de Android, el archivo aidl debe agregarse manualmente a LOCAL_SRC_FILES antes de que pueda procesarse. Según la extensión aidl, el sistema de compilación utilizará una herramienta aidl especial para procesarlo y generar automáticamente archivos de código Java. Como sigue:

LOCAL SRC FILES += \
		{
    
    path}/*.aidl \

1.3 El uso de Binder por la capa de marco y la capa de aplicación

  El mecanismo de comunicación basado en Binder de la capa de Java se puede utilizar tanto en la capa de marco de Java como en la capa de aplicación de Java. Los usuarios deben utilizar el archivo aidl para definir la interfaz. La diferencia radica en la estructura del servidor y el cliente. La situación típica de usar Binder en la capa del marco de trabajo de Java es: después de que el servidor completa una clase IXXXStub, necesita usar la clase ServiceManager para agregarla a un servicio con un nombre determinado; el cliente también obtiene este servicio a través de ServiceManager de acuerdo con el nombre, y lo convierte en una interfaz tipo IXXX para realizar una llamada.
  La situación típica de usar Binder en la capa de aplicación de Java es: el servidor declara un componente de servicio y, en su método onBind0, devuelve un heredero IXXXStub como un tipo de IBinder; después de que el componente de cliente se conecta al componente de servicio a través de bindService0, puede obtener el identificador IBinder del implementador de la interfaz ServiceConnection (paquete android.content), conviértalo en tipo IXXX y luego llámelo.
  Los dos casos anteriores son el uso típico de Binder, el servidor y el cliente están en la capa del marco o ambos en la capa de la aplicación. Además, hay algunos casos especiales.
La capa del marco de trabajo de Java puede definir el componente de servicio, solo declárelo en el archivo AndroidMenifest.xml del paquete de recursos. Si el cliente llama al componente Servicio de la capa del marco, es básicamente lo mismo que el de la capa de aplicación típica. Después de que la interfaz aidl definida por la capa del marco de Java se implementa generalmente en el paquete de servicios de Java, el paquete de la aplicación puede hacer referencia directamente al archivo aidl y llamar directamente a la implementación del servicio en la capa del marco. Este método se puede implementar, pero no se usa comúnmente.

2 Uso de Binder de la capa del marco Java

2.1 Registro de ServiceManager y servicio

  La característica de usar Binder en la capa de marco de Java es que necesita registrarse y obtener servicios a través de ServiceManager. El nombre del servicio es el enlace entre el cliente y el servidor.
La clase ServiceManager es una clase interna en android.os, ubicación del código: frameworks/base/core/java/android/os/ServiceManager.java

public final class ServiceManager {
    
    
    private static final String TAG = "ServiceManager";

    private static IServiceManager sServiceManager;
    private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();

    private static IServiceManager getIServiceManager() {
    
    
        if (sServiceManager != null) {
    
    
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
        return sServiceManager;
    }
	//获得服务
    public static IBinder getService(String name) {
    
    
        try {
    
    
            IBinder service = sCache.get(name);
            if (service != null) {
    
    
                return service;
            } else {
    
    
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

	//增加服务
    public static void addService(String name, IBinder service) {
    
    
        try {
    
    
            getIServiceManager().addService(name, service, false);
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "error in addService", e);
        }
    }

    public static void addService(String name, IBinder service, boolean allowIsolated) {
    
    
        try {
    
    
            getIServiceManager().addService(name, service, allowIsolated);
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "error in addService", e);
        }
    }

    //检查服务
    public static IBinder checkService(String name) {
    
    
        try {
    
    
            IBinder service = sCache.get(name);
            if (service != null) {
    
    
                return service;
            } else {
    
    
                return getIServiceManager().checkService(name);
            }
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "error in checkService", e);
            return null;
        }
    }

    //列出服务
    public static String[] listServices() {
    
    
        try {
    
    
            return getIServiceManager().listServices();
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "error in listServices", e);
            return null;
        }
    }

    public static void initServiceCache(Map<String, IBinder> cache) {
    
    
        if (sCache.size() != 0) {
    
    
            throw new IllegalStateException("setServiceCache may only be called once");
        }
        sCache.putAll(cache);
    }
}

  La clase ServiceManager contiene algunos métodos estáticos. El llamado servicio es un tipo IBinder, normalmente un heredero del tipo IXXX.Stub, que representa una implementación de servicio.
  Por lo general, el servidor usa addService() para agregar servicios y el cliente usa getService() para obtener y usar los servicios. El servidor generalmente lo implementa y registra la biblioteca de servicios. Generalmente, un servicio es una clase en el paquete com.android.server de la biblioteca de servicios de Java.
  La clase ServiceManager de la capa de Java se implementa realmente en función del administrador de servicios de la capa local, y el contenido registrado se administra de manera uniforme mediante el proceso del daemon del administrador de servicios local.

2.2 Detalles constructivos de Binder

  El contenido principal del uso de Binder en la capa del framework Java es el archivo aidl, el servidor y el cliente, más la adición de servicios y la adquisición de dos entornos.
Los detalles de uso de Binder, el procesamiento de archivos aidl y la generación de clases automáticas se muestran en la Figura 7-4.
inserte la descripción de la imagen aquí
  El contenido de la línea de puntos en la figura son las clases existentes en la capa del marco de trabajo de Android. IInterface e IBinder son dos interfaces, y Binder es una clase abstracta que implementa la interfaz de IBinder.
  Primero, debe escribir un archivo de descripción de interfaz llamado IXXX.aidl, que define una serie de métodos. Una vez procesado el archivo aidl, se generará automáticamente el archivo IXXX.java. IXXX en el archivo IXXX.java es una interfaz que implementa IInterface y contiene los métodos definidos en IXXX, IXXX.Stub es una clase abstracta, por un lado, hereda la clase Binder, por otro lado, implementa el Interfaz IXXX, pero los métodos en IXXX no están implementados.
  El servidor debe proporcionar una clase que herede IXXX.Stub e implemente los métodos definidos en IXXX. Por lo tanto, esta clase en realidad implementa la interfaz IBinder indirectamente, y una instancia de esta clase se puede registrar como un servicio con un nombre determinado a través del método addService() de ServiceManager.
  El cliente necesita obtener el identificador de IBinder según el nombre a través del método getService() de ServiceManager, y luego convertir este IBinder en tipo IXXX a través del método estático asInterface() en IXXX.Stub, y luego llamar a los métodos en él.
  Tomando como ejemplo AlarmManagerService en la capa del marco de Java, el lado del servidor es parte de la biblioteca de servicios, mientras que el lado del cliente es parte de la capa del marco. Su interfaz se define en la capa del marco y la ruta del archivo es frameworks/base/core/java/android/app/IAlarmManager.aidl

package android.app;

import android.app.AlarmManager;
import android.app.IAlarmListener;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.WorkSource;

/**
 * System private API for talking with the alarm manager service.
 *
 * {@hide}
 */
interface IAlarmManager {
    
    
    /** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
    void set(String callingPackage, int type, long triggerAtTime, long windowLength,
            long interval, int flags, in PendingIntent operation, in IAlarmListener listener,
            String listenerTag, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock);
    boolean setTime(long millis);
    void setTimeZone(String zone);
    void remove(in PendingIntent operation, in IAlarmListener listener);
    long getNextWakeFromIdleTime();
    AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
    // update the uids being synchronized by network socket request manager
    void updateBlockedUids(int uid, boolean isBlocked);
}

  El contenido aquí define la interfaz denominada IAlarmManager, que se encuentra en el paquete android.app. ¡Aviso! Esta no es una interfaz simple en Java, generará contenido complejo.
La implementación del servidor está en el paquete del servidor, la ruta es: frameworks/base/services/core/java/com/android/server/AlarmManagerService.java

class AlarmManagerService extends SystemService {
    
    
......
    private final IBinder mService = new IAlarmManager.Stub() {
    
    
	......
	        @Override
        public boolean setTime(long millis) {
    
    
            getContext().enforceCallingOrSelfPermission(
                    "android.permission.SET_TIME",
                    "setTime");

            if (mNativeData == 0) {
    
    
                Slog.w(TAG, "Not setting time since no alarm driver is available.");
                return false;
            }

            synchronized (mLock) {
    
    
                return setKernelTime(mNativeData, millis) == 0;
            }
        }

        @Override
        public void setTimeZone(String tz) {
    
    
            getContext().enforceCallingOrSelfPermission(
                    "android.permission.SET_TIME_ZONE",
                    "setTimeZone");

            final long oldId = Binder.clearCallingIdentity();
            try {
    
    
                setTimeZoneImpl(tz);
            } finally {
    
    
                Binder.restoreCallingIdentity(oldId);
            }
        }
	......
	}
......
}

  El IBinder mService final privado en AlarmManagerService implementa la clase IAlarmManager.Stub, que tiene varios métodos definidos en el archivo AlarmManager.aidl, que se implementan en la clase AarmManagerService.
  El registro de AlarmManagerService se realiza en el archivo frameworks/base/services/java/com/android/server/SystemServer.java de la siguiente manera:

            traceBeginAndSlog("StartAlarmManagerService");
            mSystemServiceManager.startService(AlarmManagerService.class);
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

  El lado del cliente es parte de la biblioteca del marco de Java. El proceso de obtención de la interfaz según el nombre del servicio se encuentra en el archivo ContextImpl.java en el directorio core/java/android/app/. Los fragmentos de contenido son los siguientes:

AlarmManager mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);

  El cliente llama al método getSystemService() de Context y obtiene un identificador de tipo AlarmManager a través del parámetro Context.ALARM_SERVICE. Una vez que el cliente obtiene el identificador del tipo AlarmManager, puede llamar a los métodos que contiene.

  AlarmManager es en realidad solo un envoltorio alrededor de IAlarmManager. Tiene un método con el mismo nombre y se implementa llamando al contenido en IAlarmManager, que también es la API externa del administrador de alarmas. Cuando cada parte llama a AlarmManager, en realidad tiene una relación de llamada remota con AlarmManagerService implementado en la biblioteca de servicios.
  Las varias partes anteriores son todos los contenidos que deben construirse manualmente cuando se usa el mecanismo Binder en la capa del marco de Java. Además, de acuerdo con el archivo IAlarmManager.adil, el archivo de código fuente de Java se generará automáticamente y su ruta es la siguiente:

out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/app/IAlarmManager.java

La estructura básica del archivo IAlarmManager.java es la siguiente:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: frameworks/base/core/java/android/app/IAlarmManager.aidl
 */
package android.app;
/**
 * System private API for talking with the alarm manager service.
 *
 * {@hide}
 */
public interface IAlarmManager extends android.os.IInterface
{
    
    
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.app.IAlarmManager
{
    
    
private static final java.lang.String DESCRIPTOR = "android.app.IAlarmManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
    
    
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an android.app.IAlarmManager interface,
 * generating a proxy if needed.
 */
public static android.app.IAlarmManager asInterface(android.os.IBinder obj)
{
    
    
if ((obj==null)) {
    
    
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.app.IAlarmManager))) {
    
    
return ((android.app.IAlarmManager)iin);
}
return new android.app.IAlarmManager.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
    
    
return this;
}
@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;
}
case TRANSACTION_set:
{
    
    
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _arg1;
_arg1 = data.readInt();
long _arg2;
_arg2 = data.readLong();
long _arg3;
_arg3 = data.readLong();
long _arg4;
_arg4 = data.readLong();
int _arg5;
_arg5 = data.readInt();
android.app.PendingIntent _arg6;
if ((0!=data.readInt())) {
    
    
_arg6 = android.app.PendingIntent.CREATOR.createFromParcel(data);
}
else {
    
    
_arg6 = null;
}
android.app.IAlarmListener _arg7;
_arg7 = android.app.IAlarmListener.Stub.asInterface(data.readStrongBinder());
java.lang.String _arg8;
_arg8 = data.readString();
android.os.WorkSource _arg9;
if ((0!=data.readInt())) {
    
    
_arg9 = android.os.WorkSource.CREATOR.createFromParcel(data);
}
else {
    
    
_arg9 = null;
}
android.app.AlarmManager.AlarmClockInfo _arg10;
if ((0!=data.readInt())) {
    
    
_arg10 = android.app.AlarmManager.AlarmClockInfo.CREATOR.createFromParcel(data);
}
else {
    
    
_arg10 = null;
}
this.set(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10);
reply.writeNoException();
return true;
}
case TRANSACTION_setTime:
{
    
    
data.enforceInterface(DESCRIPTOR);
long _arg0;
_arg0 = data.readLong();
boolean _result = this.setTime(_arg0);
reply.writeNoException();
reply.writeInt(((_result)?(1):(0)));
return true;
}
case TRANSACTION_setTimeZone:
{
    
    
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setTimeZone(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_remove:
{
    
    
data.enforceInterface(DESCRIPTOR);
android.app.PendingIntent _arg0;
if ((0!=data.readInt())) {
    
    
_arg0 = android.app.PendingIntent.CREATOR.createFromParcel(data);
}
else {
    
    
_arg0 = null;
}
android.app.IAlarmListener _arg1;
_arg1 = android.app.IAlarmListener.Stub.asInterface(data.readStrongBinder());
this.remove(_arg0, _arg1);
reply.writeNoException();
return true;
}
case TRANSACTION_getNextWakeFromIdleTime:
{
    
    
data.enforceInterface(DESCRIPTOR);
long _result = this.getNextWakeFromIdleTime();
reply.writeNoException();
reply.writeLong(_result);
return true;
}
case TRANSACTION_getNextAlarmClock:
{
    
    
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
android.app.AlarmManager.AlarmClockInfo _result = this.getNextAlarmClock(_arg0);
reply.writeNoException();
if ((_result!=null)) {
    
    
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
    
    
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements android.app.IAlarmManager
{
    
    
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;
}
/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
@Override public void set(java.lang.String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, android.app.PendingIntent operation, android.app.IAlarmListener listener, java.lang.String listenerTag, android.os.WorkSource workSource, android.app.AlarmManager.AlarmClockInfo alarmClock) 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.writeString(callingPackage);
_data.writeInt(type);
_data.writeLong(triggerAtTime);
_data.writeLong(windowLength);
_data.writeLong(interval);
_data.writeInt(flags);
if ((operation!=null)) {
    
    
_data.writeInt(1);
operation.writeToParcel(_data, 0);
}
else {
    
    
_data.writeInt(0);
}
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
_data.writeString(listenerTag);
if ((workSource!=null)) {
    
    
_data.writeInt(1);
workSource.writeToParcel(_data, 0);
}
else {
    
    
_data.writeInt(0);
}
if ((alarmClock!=null)) {
    
    
_data.writeInt(1);
alarmClock.writeToParcel(_data, 0);
}
else {
    
    
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_set, _data, _reply, 0);
_reply.readException();
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
}
@Override public boolean setTime(long millis) throws android.os.RemoteException
{
    
    
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
    
    
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeLong(millis);
mRemote.transact(Stub.TRANSACTION_setTime, _data, _reply, 0);
_reply.readException();
_result = (0!=_reply.readInt());
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void setTimeZone(java.lang.String zone) 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.writeString(zone);
mRemote.transact(Stub.TRANSACTION_setTimeZone, _data, _reply, 0);
_reply.readException();
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
}
@Override public void remove(android.app.PendingIntent operation, android.app.IAlarmListener 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);
if ((operation!=null)) {
    
    
_data.writeInt(1);
operation.writeToParcel(_data, 0);
}
else {
    
    
_data.writeInt(0);
}
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_remove, _data, _reply, 0);
_reply.readException();
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
}
@Override public long getNextWakeFromIdleTime() throws android.os.RemoteException
{
    
    
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
long _result;
try {
    
    
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getNextWakeFromIdleTime, _data, _reply, 0);
_reply.readException();
_result = _reply.readLong();
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) throws android.os.RemoteException
{
    
    
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.app.AlarmManager.AlarmClockInfo _result;
try {
    
    
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(userId);
mRemote.transact(Stub.TRANSACTION_getNextAlarmClock, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
    
    
_result = android.app.AlarmManager.AlarmClockInfo.CREATOR.createFromParcel(_reply);
}
else {
    
    
_result = null;
}
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_set = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_setTimeZone = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_remove = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_getNextWakeFromIdleTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_getNextAlarmClock = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
}
/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
public void set(java.lang.String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, android.app.PendingIntent operation, android.app.IAlarmListener listener, java.lang.String listenerTag, android.os.WorkSource workSource, android.app.AlarmManager.AlarmClockInfo alarmClock) throws android.os.RemoteException;
public boolean setTime(long millis) throws android.os.RemoteException;
public void setTimeZone(java.lang.String zone) throws android.os.RemoteException;
public void remove(android.app.PendingIntent operation, android.app.IAlarmListener listener) throws android.os.RemoteException;
public long getNextWakeFromIdleTime() throws android.os.RemoteException;
public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) throws android.os.RemoteException;
}

El contenido definido en el archivo IAlarmManager.java incluye principalmente las siguientes partes:

  • La interfaz IAlarmManager implementa android.os.IInterface y declara métodos como set() y setRepeating() definidos en IAlarmManager.aidl.
  • IAlarmManager.Stub es una clase estática abstracta que hereda de android.os.Binder e implementa la propia interfaz IAlarmManager.
  • Hay un método fijo asInterface() en IAlarmManager.Stub, el tipo de parámetro es IBinder y el tipo de retorno es IAlarmListener.
  • Hay muchos métodos dentro de la clase IAlarmManager.Stub, que realizan la comunicación basada en el mecanismo Binder. El proceso de generación automática de archivos se completa con herramientas y no requiere que los desarrolladores lo manejen. Por lo tanto, usar el mecanismo Binder en la capa de Java no necesita lidiar con la parte de comunicación más complicada.

3 Tipos de parámetros y valores de retorno en Binder

3.1 Resumen de tipos

  Porque la interfaz de Binder específica de la capa de Java generalmente se describe mediante un archivo aidl. El método definido por la interfaz involucra la cuestión del parámetro y el tipo de valor devuelto. Los métodos de la interfaz aidl no son aplicables a todos los tipos de Java y están esencialmente limitados por los tipos que se pueden transferir en Parcel.

Los tipos que se pueden utilizar en aidl son los siguientes aspectos:

  • Tipos básicos del lenguaje Java (boolean, byte, char, short, int, float y double)
  • CharSequence y String en java.lang
  • Lista y Map<K, V> en java.util
  • Interfaces definidas en otros archivos aidl (requiere importación)
  • Clases implementadas por Parcelable (requiere importación)

  Los tipos básicos de Java pueden ser compatibles directamente con Parcel y se pueden usar como parámetros y tipos de valores de retorno de los métodos de la interfaz aidl, sin necesidad de importarlos. Aparentemente, Java también puede usar sus tipos de matriz.
  CharSequence y String son clases que representan cadenas, que se pueden usar como parámetros y tipos de valor de retorno de los métodos de la interfaz aidl, y no es necesario importarlas. List y Map<K, V> son clases de plantilla del tipo contenedor. Puede usar estas dos plantillas para encapsular otros tipos, como los tipos de parámetro y valor de retorno de los métodos de la interfaz aidl, sin necesidad de importar.
  Además de las situaciones anteriores, también puede usar interfaces definidas en otros archivos y clases aidl implementados a través de Parcelable, los cuales requieren el uso de clases de importación.

3.2 Interfaces definidas en otros archivos aidl como tipos

  La interfaz definida en el archivo aidl esencialmente formará un implementador de la interfaz IInterface y se puede convertir en un tipo IBinder para su uso. Por lo tanto, la esencia de usar "interfaz definida en otros archivos aidl" como parámetro y tipo de retorno del método de la interfaz aidl es usar el tipo IBinder para transferir en Parcel.
  En la comunicación entre procesos de Binder, si necesita usar un mecanismo similar a la devolución de llamada, generalmente necesita usar otro archivo aidl para representar la interfaz de devolución de llamada y usarlo como el tipo de parámetro del método en el archivo aidl de la interfaz principal .
  Por ejemplo, en el directorio frameworks/core/java/android/app/ en el código de la biblioteca Java Framework, hay dos archivos aidl relacionados con el administrador de fondos de pantalla, uno representa la interfaz principal (IWallpaperManager.aidl) y el otro representa la interfaz de devolución de llamada (IWallpaperManagerCallback. aidl).

package android.app;
oneway interface IWallpaperManagerCallback {
    
    
    /**
     * Called when the wallpaper has changed
     */
    void onWallpaperChanged();
}

Un fragmento de la referencia de IWallpaperManager.aidl a este tipo de devolución de llamada se ve así:

package android.app;

import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.app.IWallpaperManagerCallback;
import android.app.WallpaperInfo;
import android.content.ComponentName;

/** @hide */
interface IWallpaperManager {
    
    
......
    /**
     * Get the wallpaper for a given user.
     */
    ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, int which,
            out Bundle outParams, int userId);
......
}

  Como se usa IWallpaperManagerCallback como tipo de parámetro, debe usar import para importar esta interfaz, y el nombre importado es el nombre del paquete más el nombre de la interfaz aidl.
  La interfaz IWallpaperManager en realidad está implementada por una clase en la biblioteca de servicios de Java, que está integrada con otro contenido de ayuda en la capa del marco. IWallpaperManagerCallback se implementa en la biblioteca de marco de Java ( WallpaperManager.java ) y se pasa como parámetro a la interfaz IWallpaperManager. En el proceso de ejecución real, la implementación de IWallpaperManager en la biblioteca de servicios de Java llama al método onWallpaperChanged() implementado en la biblioteca de marco de Java a través de IWallpaperManagerCallback, que desempeña una función de notificación.

3.3 El protocolo Parcelable como tipo

  Otro tipo de contenido que se puede utilizar como parámetro y tipo de valor de retorno del método de la interfaz aidl es "clase basada en el protocolo Parcelable". El protocolo Parcelable significa que Parcel se puede usar para escribir y restaurar, y el protocolo Parcelable se implementa cuando se implementa la interfaz Parcelable.

Parcelable es la interfaz de Parcelable en el paquete android.os, y su estructura básica es la siguiente:

public interface Parcelable {
    
    

    public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
    public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002;
    public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
    public int describeContents();
    public void writeToParcel(Parcel dest, int flags);		//	写入Parcel的方法

    public interface Creator<T> {
    
    							//表示“创建者”的子类
        public T createFromParcel(Parcel source);
        public T[] newArray(int size);
    }

    public interface ClassLoaderCreator<T> extends Creator<T> {
    
    
        public T createFromParcel(Parcel source, ClassLoader loader);
    }
}

Hacer que una clase soporte el protocolo Parcelable tiene principalmente los siguientes pasos:

  1. La clase implementa la interfaz Parcelable.
  2. Utilice el método void writeToParcel(Parcel out) para completar la operación de escritura de parámetros.
  3. Cree un creador implementando la interfaz Parcelable.Creator para realizar la función de creación estática
  4. Se puede crear un archivo aidl como interfaz (opcional).

En el proceso de aplicación real, las clases basadas en el protocolo Parcelable generalmente se usan para pasar una clase que representa una estructura de datos compleja. Un ejemplo de implementación se ve así:

public final class Rect implements Parcelable {
    
    
	public int left;		//各个类的域
	public int top;
	public int right;
	public int bottom;

	public static final Parcelable.Creator<Rect> CREATOR 
									= new Parcelable.Creator<Rect>() {
    
    
		public Rect createFromParcel(Parcel in)(	//实现创建方法
			return new Rect(in);
		}
		
		public Rect[] newArray(int size) {
    
    			//实现建立数组方法
			return new Rect[sizel;
		}
	};
	
	public Rect() {
    
    }
	public void writeToParcel(Parcelout) {
    
    
		//依次写入各个成员
		out.writeInt(top);
		out.writeInt(left);
		out.writeInt(right);
		out.writeInt(bottom);
	}
	public void readFromParcel(Parcel in) {
    
    
		//依次读出各个成员
		left = in.readInt();
		top = in.readInt();
		right = in.readInt();
		bottom = in.readInt();
	}
}

  La esencia de la clase Rect es una clase que representa un rectángulo, que contiene 4 dominios (atributos) de tipo entero. Sin embargo, la estructura de esta clase no es complicada. Pero si desea transferir dicha clase en el proceso de transferencia de Binder, obviamente, Parcel no puede saber directamente cuál es el contenido específico que se transferirá en él. El propósito de hacer que Rect implemente la interfaz Parcelable es establecer un protocolo Parcel y transferir estructuras de datos específicas de acuerdo con este protocolo . La esencia del protocolo Parcelabl es permitir que las clases que implementan la interfaz Parcelable decidan qué contenido se debe pasar llamando al método writeToParcel().
  Hay muchas clases que implementan la interfaz Parcelable en la capa del marco de trabajo de Android, que se pueden usar directamente en el archivo aidl como tipo de parámetros y valores de retorno .
La clase Bundle en el paquete android.os es un implementador de la interfaz Parcelable y su estructura es la siguiente:

public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
    
    
......
	//实现Creator<>
    public static final Parcelable.Creator<Bundle> CREATOR =
        new Parcelable.Creator<Bundle>() {
    
    
        @Override
        public Bundle createFromParcel(Parcel in) {
    
    
            return in.readBundle();
        }

        @Override
        public Bundle[] newArray(int size) {
    
    
            return new Bundle[size];
        }
    };

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
    
    
        final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
        try {
    
    
            super.writeToParcelInner(parcel, flags);
        } finally {
    
    
            parcel.restoreAllowFds(oldAllowFds);
        }
    }
......
}

Para ser referenciado por otras clases, cree un archivo frameworks/base/core/java/android/os/Bundle.aidl para la clase Bundle, el contenido es el siguiente:

package android.os;
parcelable Bundle;

  El contenido anterior indica que la clase Bundle en el paquete android.os es el implementador de Parcelable. En el archivo aidl, las clases pueden importar Bundle como el tipo de sus parámetros y devolver valores a través de las siguientes declaraciones:

import android.os.Bundle;

  Por lo tanto, las clases que implementan la interfaz Parcelable en la capa del marco de trabajo de Android se pueden usar directamente en el archivo aidl. En el proceso de desarrollo real, es raro que realmente se necesite crear una clase que implemente el protocolo Parcelable. Las razones principales son:

  • Para la transferencia de tipos de datos complejos, se puede dividir en varios parámetros en el método;
  • Use Bundle, Uri, Bitmap y otras clases que han implementado Parcelable, especialmente Bundle, que puede contener cualquier cantidad de información en forma de clave-valor y puede completar la descripción de estructuras de datos complejas. Solo cuando existe una "estructura de datos" especial en el programa que debe describirse por separado, es necesario implementar el protocolo Parcelable.

Supongo que te gusta

Origin blog.csdn.net/Xiaoma_Pedro/article/details/130961012
Recomendado
Clasificación