Competencia en el servicio de Android

Introducción a esta sección:

En esta sección, continuamos estudiando el componente Service (servicio). Esta sección aprenderá algunos conceptos de la comunicación entre procesos AIDL en Android. No profundiza en el nivel de código fuente. Si sabe lo que es para el momento siendo, usted puede usarlo!


1. La introducción inicial del mecanismo Binder

1) ¿Qué son IBinder y Binder?

Echemos un vistazo a lo que dice la documentación oficial:

Traducción china:

IBinder es la interfaz básica de los objetos remotos y es la parte central del mecanismo ligero de llamadas remotas diseñado para un alto rendimiento. Pero no solo se usa para llamadas remotas, sino también para llamadas en proceso. Esta interfaz define un protocolo para interactuar con objetos remotos. Pero no implemente directamente esta interfaz, pero herede (extienda) Binder .

La API principal de IBinder es transact() y la API correspondiente es Binder.onTransact() . A través del primero, puede enviar y emitir llamadas al objeto IBinder remoto, y el segundo permite que su objeto remoto responda a las llamadas recibidas. Las API de IBinder se  ejecutan todas en Syncronous (síncrono) , por ejemplo, transact() no regresa hasta que se llama al método Binder.onTransact() de la otra parte . Este es ciertamente el caso cuando la llamada ocurre dentro de un proceso, y el mismo efecto ocurre cuando es entre procesos, con la ayuda de IPC .

Los datos enviados a través de transact() son Parcel . Parcel es un búfer general con algunos metadatos que describen su contenido además de los datos. Los metadatos se utilizan para administrar las referencias a los objetos de IBinder para que estas referencias se puedan conservar a medida que los búferes se mueven de un proceso a otro. Esto garantiza que cuando un IBinder se escribe en Parcel y se envía a otro proceso, si otro proceso envía una referencia al mismo IBinder al proceso original, entonces el proceso original puede recibir las referencias de IBinder enviadas. Este mecanismo permite gestionar IBinder y Binder entre procesos como identificadores únicos.

El sistema mantiene un grupo de subprocesos para almacenar subprocesos interactivos para cada proceso. Estos subprocesos interactivos se utilizan para enviar todas las llamadas IPC de otros procesos. Por ejemplo: cuando se envía un IPC del proceso A al proceso B, el subproceso de llamada en A (que no debe estar en el grupo de subprocesos) se bloquea en transact() . Un subproceso en el grupo de subprocesos interactivos en el proceso B recibe esta llamada, llama a  Binder.onTransact() y devuelve un paquete como resultado después de la finalización. Luego, el subproceso en espera en el proceso A puede continuar ejecutándose después de recibir el paquete devuelto. De hecho, otro proceso parece un subproceso del proceso actual, pero no fue creado por el proceso actual.

El mecanismo Binder también admite llamadas recursivas entre procesos. Por ejemplo, el proceso A ejecuta su propio IBinder's transact() para llamar al Binder del proceso B, y el proceso B usa transact() para llamar al proceso A en su Binder.onTransact(), luego el proceso A espera que la llamada que emitió regrese a At al mismo tiempo, se usará Binder.onTransact() para responder al transact() del proceso B. En resumen, el resultado de Binder es que sentimos que no hay diferencia entre las llamadas entre procesos y las llamadas en proceso.

Al manipular objetos remotos, a menudo es necesario comprobar que son válidos. Hay tres formas de hacerlo:

  • 1 El método transact() arrojará una excepción remota cuando el proceso donde se encuentra el IBinder no existe.
  • 2 Si el proceso de destino no existe, devuelve falso al llamar a pingBinder().
  • 3 Puede utilizar el método linkToDeath() para registrar un IBinder.DeathRecipient con IBinder, que se llamará cuando finalice el proceso representado por IBinder.

PD: La traducción al chino está tomada de:  Android Development: What is IBinder

Bueno, se estima que puede estar confundido después de leer esta lista de cosas, aquí hay un breve resumen:

IBinder es una interfaz para la comunicación entre procesos proporcionada por Android, y generalmente no implementamos esta interfaz directamente, ¡  pero implementamos la comunicación entre procesos heredando la clase Binder! ¡Es una forma de implementar IPC (comunicación entre procesos) en Android!


2) Análisis del mecanismo de unión

El mecanismo de Binder en Android consta de una serie de componentes del sistema:  Cliente, Servidor, Administrador de servicios y controlador de Binder.

El proceso de llamada aproximado es el siguiente, y el Administrador de servicios es más complicado, ¡así que no lo estudiaré en detalle aquí!

Análisis de proceso:

->  Cuando el Cliente llama a un método en una interfaz de proxy, el método de la interfaz de proxy empaquetará los parámetros pasados ​​por el Cliente en un objeto Parcel -> Luego,
la  interfaz proxy envía el objeto Parcel al controlador Binder en el kernel;
->  Luego, el servidor leerá los datos de la solicitud en el controlador Binder, si se envía a sí mismo, desempaquetará el objeto Parcel, procesará y devolverá el resultado; PD:
los métodos definidos en la interfaz del proxy y los métodos definidos en el servidor son correspondencia uno a uno, además , todo el proceso de llamada es síncrono, es decir, cuando el Servidor está procesando, ¡el Cliente será bloqueado (bloqueado)!Y la definición de la interfaz proxy mencionada aquí es AIDL (Lenguaje de descripción de la interfaz de Android) que se mencionará más adelante.


3) ¿Por qué Android usa el mecanismo Binder para lograr la comunicación entre procesos?

  1. Fiabilidad : en los dispositivos móviles, el método de comunicación basado en Cliente-Servidor se suele utilizar para realizar la comunicación interna entre Internet y el dispositivo. Actualmente, Linux admite IPC, incluidas las canalizaciones tradicionales, System V IPC, es decir, cola de mensajes/memoria compartida/semáforo, y solo el socket admite el modo de comunicación Cliente-Servidor entre sockets. El sistema Android proporciona a los desarrolladores ricas interfaces funcionales para la comunicación entre procesos, reproducción de medios, sensores y transmisión inalámbrica. Estas funciones son gestionadas por diferentes servidores. El desarrollo solo se preocupa de establecer la comunicación entre el cliente y el servidor de su propia aplicación, y luego puede utilizar este servicio. No hay duda de que si se configura un conjunto de protocolos en la capa inferior para realizar la comunicación Cliente-Servidor, la complejidad del sistema aumentará. Al darse cuenta de un entorno tan complejo en un teléfono móvil con recursos limitados, la confiabilidad es difícil de garantizar.
  2. Rendimiento de transmisión : el zócalo se utiliza principalmente para la comunicación entre procesos a través de la red y la comunicación entre procesos en la máquina, pero la eficiencia de transmisión es baja y la sobrecarga es alta. La cola de mensajes y la canalización adoptan el método de almacenar y reenviar, es decir, los datos primero se copian del búfer del remitente a un búfer creado por el kernel y luego se copian del búfer del kernel al búfer del receptor. dos copias en el proceso. Aunque no es necesario copiar la memoria compartida, el control es complicado. Compare los tiempos de copia de datos de varios métodos IPC. Memoria compartida: 0 veces. Aglutinante: 1 vez. Socket/tubería/cola de mensajes: 2 veces.
  3. Seguridad : Android es una plataforma abierta, por lo que es importante garantizar la seguridad de la aplicación. Android asigna UID/PID a cada aplicación instalada y el UID del proceso se puede usar para identificar la identidad del proceso. Tradicionalmente, solo el usuario puede completar el UID/PID en el paquete de datos, que no es confiable y es fácil de usar por programas maliciosos. En cambio, requerimos que el kernel agregue UID confiables. Por lo tanto, por confiabilidad, transmisión y seguridad. Android ha establecido un nuevo conjunto de métodos de comunicación entre procesos. ——Extracto de: Una breve comprensión del mecanismo Binder en Android

Por supuesto, como desarrollador junior, no nos importa lo anterior. El beneficio más directo que nos brinda el mecanismo Binder es que  no necesitamos preocuparnos por cómo se implementa la capa inferior. Solo necesitamos personalizar un archivo de interfaz de acuerdo con las reglas de AIDL,  y luego llame Al llamar al método en la interfaz, ¡se puede completar la comunicación entre los dos procesos!


2. Detalles de uso de AIDL


1) ¿Qué es AIDL?

Oye, mencionamos el término IPC anteriormente , su nombre completo es: comunicación entre procesos (interprocess communication) , porque en el sistema Android, cada programa de aplicación se ejecuta en su propio proceso, y generalmente es imposible comunicarse directamente entre procesos. y para lograr el cruce de procesos, Android nos proporciona el mecanismo Binder mencionado anteriormente, y el lenguaje de interfaz que utiliza este mecanismo es:  AIDL (Lenguaje de definición de interfaz de Android), su sintaxis es muy simple, y este lenguaje de interfaz no es el ¡El lenguaje de programación real simplemente define la interfaz de comunicación entre dos procesos! El código Java generado conforme al protocolo de comunicación lo genera la herramienta aidl.exe en el directorio de herramientas de la plataforma del SDK de Android , y el archivo de interfaz correspondiente se genera en el directorio :gen, generalmente la interfaz de: ¡Xxx.java! La interfaz contiene una clase interna Stub , que implementa la interfaz IBinder y la interfaz de comunicación personalizada en esta clase. Esta clase se usará como la clase de devolución de llamada del Servicio remoto, implementando la interfaz IBinder, por lo que se puede usar como el valor de retorno de el método onBind( ) del Servicio!


2) AIDL implementa una comunicación simple entre dos procesos

Antes de comenzar a escribir archivos de interfaz AIDL, debemos comprender algunas consideraciones para escribir AIDL:

Notas de AIDL:

  • El nombre de la interfaz debe ser el mismo que el nombre del archivo aidl
  • No agregue modificadores de acceso delante de las interfaces y métodos : público, privado, protegido, etc., ¡y no se puede usar el final estático!
  • Los tipos admitidos por AIDL de forma predeterminada incluyen tipos básicos de Java , String , List , Map , CharSequence y otros tipos que requieren declaraciones de importación. Para usar tipos personalizados como parámetros o valores de retorno, los tipos personalizados deben implementar la interfaz Parcelable. Detalles Ver paso complejo tipos de datos más tarde
  • Los tipos definidos por el usuario y otros tipos de interfaz generados por AIDL deben importarse explícitamente en el archivo de descripción de aidl, incluso si la clase y el paquete definido están en el mismo paquete.

Además, si el compilador que usa para escribir aidl es: Eclipse, debe prestar atención: ¡No cree directamente un nuevo archivo y luego compílelo!¡En este caso, no puede abrir el archivo, por lo que no puede escribir código!
①Cree un nuevo archivo txt directamente, guárdelo como formato .aidl después de escribirlo y luego cópielo en la ruta correspondiente
②Debido a que aidl es similar a la interfaz, cree directamente una nueva interfaz, después de escribir el contenido, vaya al directorio donde el archivo java correspondiente se encuentra para modificar el nombre del sufijo del archivo;

Si está utilizando Android Studio, a diferencia de Eclipse, si crea un archivo AIDL de acuerdo con Eclipse, encontrará que el archivo XXX.java correspondiente no está compilado ni generado. Para crear AIDL en AS, debe crear una nueva carpeta aidl en el directorio principal. , y luego defina un paquete con el mismo nombre que el paquete aidl, y finalmente cree un archivo aidl, y luego presione ctrl + f9 para recompilar, ¡estará bien!

Los resultados de la compilación exitosa de los dos anteriores son los siguientes, puede encontrar los archivos AIDL correspondientes en los directorios correspondientes


1. Servidor:

Paso 1: Crear archivo AIDL:

IPerson.aidl

paquete com.jay.aidl; 

interfaz IPerson { 
    String queryPerson(int num); 
}

Abramos IPerson.java para ver el código interno:

IPerson.java

/* 
 * Este archivo se genera automáticamente. NO MODIFICAR. 
 * Archivo original: C:\\Code\\ASCode\\AIDLServer\\app\\src\\main\\aidl\\com\\jay\\aidl\\IPerson.aidl 
 */ 
paquete com.jay.aidl; 
interfaz pública IPerson extiende android.os.IInterface 
{ 
/** Clase de código auxiliar de implementación de IPC del lado local. */ 
public static abstract class Stub extiende android.os.Binder implementa com.jay.aidl.IPerson 
{ 
private static final java.lang.String DESCRIPTOR = "com.jay.aidl.IPerson"; 
/** Construye el stub para adjuntarlo a la interfaz. */ 
public Stub() 
{ 
this.attachInterface(this, DESCRIPTOR); 
} 
/**
 * Convierta un objeto IBinder en una interfaz com.jay.aidl.IPerson, 
 * generando un proxy si es necesario. 
 */ 
public static com.jay.aidl.IPerson asInterface(android.os.IBinder obj) 
{ 
if ((obj==null)) { 
return null; 
} 
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 
if (((iin!=null)&&(iin instancia de com.jay.aidl.IPerson))) { 
return ((com.jay.aidl.IPerson)iin); 
} 
devuelve nuevo com.jay.aidl.IPerson.Stub.Proxy(obj); 
} 
@Override public android.os.IBinder asBinder() 
{ 
devuelve esto; 
}
@Override public boolean onTransact (código int, datos android.os.Parcel, respuesta android.os.Parcel, banderas int) lanza android.os.RemoteException 
{ 
cambiar (código) 
{
caso INTERFACE_TRANSACTION: 
{ 
respuesta.writeString(DESCRIPTOR); 
devolver verdadero; 
} 
case TRANSACTION_queryPerson: 
{ 
data.enforceInterface(DESCRIPTOR); 
int_arg0; 
_arg0 = datos.readInt(); 
java.lang.String _result = this.queryPerson(_arg0); 
responder.writeNoException(); 
responder.writeString(_resultado); 
devolver verdadero; 
} 
} 
return super.onTransact(código, datos, respuesta, indicadores); 
} 
proxy de clase estática privada implementa com.jay.aidl.IPerson 
mRemote = remoto; 
} 
{
privado android.os.IBinder mRemote; 
Proxy(remoto android.os.IBinder) 
{ 
@Override public android.os.IBinder asBinder() 
{ 
return mRemote; 
} 
public java.lang.String getInterfaceDescriptor() 
{ 
return DESCRIPTOR; 
} 
@Override public java.lang.String queryPerson(int num) lanza android.os.RemoteException 
{ 
android.os.Parcel _data = android.os.Parcel.obtain(); 
android.os.Parcel _reply = android.os.Parcel.obtain(); 
java.lang.String _resultado; 
prueba { 
_data.writeInterfaceToken(DESCRIPTOR); 
_datos.writeInt(num); 
mRemote.transact(Stub.TRANSACTION_queryPerson, _data, _reply, 0);
_respuesta.readException(); 
_resultado = _respuesta.readString(); 
} 
finalmente { 
_reply.recycle(); 
_datos.reciclar(); 
}
devolver _resultado; 
} 
} 
static final int TRANSACTION_queryPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 
} 
public java.lang.String queryPerson(int num) lanza android.os.RemoteException; 
}

¡Aquí solo nos enfocamos en **asInterface(IBinder)** y el método **queryPerson()** en la interfaz que definimos!

¡Este método convertirá el objeto de tipo IBinder a tipo IPerson y generará un objeto proxy para devolver el resultado si es necesario!

Podemos saltarnos los demás y pasar al siguiente paso.

Paso 2: **Personaliza nuestra clase de Servicio y completa las siguientes operaciones:

1) Heredar la clase de servicio y también personalizar una clase PersonQueryBinder para heredar la clase IPerson.Stub  es implementar la interfaz IPerson y la interfaz IBinder

2) Cree una instancia de una clase Stub personalizada, reescriba el método onBind del Servicio y devuelva un objeto de enlace.

AIDLService.java

paquete com.jay.aidlserver; 

importar android.app.Service; 
importar android.content.Intent; 
importar android.os.IBinder; 
importar android.os.RemoteException; 
importar com.jay.aidl.IPerson.Stub; 

/** 
 * Creado por Jay el 2015/8/18 0018. 
 */ 
public class AIDLService extends Service { 

    private IBinder binder = new PersonQueryBinder(); 
    cadena privada[] nombres = {"B神","艹神","基神","J神", "翔神"}; 

    consulta de cadena privada (int num) 
    { 
        if (num> 0 && num < 6) { 
            return nombres [num - 1]; 
        } 
        devuelve nulo; 
    } 

    @Override 
    public IBinder onBind(Intención intención) { 
        devuelve nulo; 
    }

    clase final privada PersonQueryBinder extiende Stub{ 
        @Override 
        public String queryPerson(int num) throws RemoteException { 
            return query(num); 
        } 
} 
    }

Paso 3: Registre el Servicio en el archivo AndroidManifest.xml

<service android:name=".AIDLService"> 
            <intent-filter> 
                <action android:name="android.intent.action.AIDLService" /> 
                <category android:name="android.intent.category.DEFAULT" /> 
            </intent-filter> 
        </service>

¡Aquí no proporcionamos la interfaz de Actividad, pero el Servicio proporcionado por la aplicación puede ser llamado por otras aplicaciones!


2. El cliente
copia directamente el archivo aidl del servidor, y luego lo completamos directamente en MainActivity, que es
algo similar a la operación de enlazar un Servicio local.El proceso es el siguiente:
1) Personalizar la clase PersonConnection para implementar el Interfaz ServiceConnection
2) Utilice el objeto PersonConnection como Parámetros, llame a bindService para vincular el servicio remoto
bindService(service, conn, BIND_AUTO_CREATE);
pd: el tercer parámetro es establecer si el servicio no se inicia, se creará automáticamente
3) A diferencia Servicio local, la conexión de servicio vinculada al servicio remoto no se puede obtener directamente
. El objeto IBinder devuelto por solo puede devolver el objeto proxy devuelto por el método onBind() , que debe procesarse de la siguiente manera: iPerson = IPerson. Stub.asInterface(servicio); ok

El código específico es el siguiente:

MainActivity.java

paquete com.jay.aidlclient; 

importar android.content.ComponentName; 
importar android.content.Intent; 
importar android.content.ServiceConnection; 
importar android.os.Bundle; 
importar android.os.IBinder; 
importar android.os.RemoteException; 
importar android.support.v7.app.AppCompatActivity; 
importar android.view.View; 
importar android.widget.Button; 
importar android.widget.EditText; 
importar android.widget.TextView; 

importar com.jay.aidl.IPerson; 

clase pública MainActivity extiende AppCompatActivity implementa View.OnClickListener{ 

    private EditText edit_num; 
    Botón privado btn_query; 
    vista de texto privada txt_name;
    iPersona privada iPersona;
    conexión de persona privada conn = nueva conexión de persona (); 


    @Override 
    protected void onCreate(Paquete de estado de instancia guardado) { 
        super.onCreate(estado de instancia guardado); 
        setContentView(R.diseño.actividad_principal); 
        enlazarVistas(); 
        //绑定远程
        Intención de servicio service = new Intent("android.intent.action.AIDLService"); 
        servicio.setPackage("com.jay.aidlserver"); 

        bindService(servicio, conexión, BIND_AUTO_CREATE); 
        btn_query.setOnClickListener(esto); 
    } 

    private void bindViews() {
    } 
        edit_num = (EditText) findViewById(R.id.edit_num);
        btn_query = (Botón) findViewById(R.id.btn_query); 
        txt_name = (TextView) findViewById(R.id.txt_name); 

    @Override 
    public void onClick(Ver v) { 
        Número de cadena = edit_num.getText().toString(); 
        int num = Integer.valueOf(number); 
        prueba { 
            txt_name.setText(iPerson.queryPerson(num)); 
        } catch (RemoteException e) { 
            e.printStackTrace(); 
        } 
        edit_num.setText(""); 
    } 

    private final class PersonConnection implementa ServiceConnection { 
        public void onServiceConnected(ComponentName name, IBinder service) { 
            iPerson = IPerson.Stub.asInterface(service); 
        } 
        public void onServiceDisconnected(ComponentName name) { 
            iPerson = null; 
        } 
} 
    }

A continuación, inicie AIDLServivce primero, luego inicie AIDLClient, ingrese el número de serie de la consulta y podrá obtener el nombre correspondiente. Por supuesto, también puede iniciar AIDLClient directamente y obtendrá el mismo efecto:

Las representaciones son las siguientes:


3) Servicio AIDL para pasar datos complejos

En el ejemplo anterior, lo que pasamos es solo un parámetro de tipo int, y luego el servidor devuelve un parámetro de tipo String, que parece satisfacer nuestras necesidades básicas, pero en el desarrollo real, es posible que debamos considerar la situación de pasar complejos ¡tipos de datos! ¡Aprendamos cómo pasar datos de tipos de datos complejos al servidor! Antes de comenzar, comprendamos la interfaz de Parcelable .

——Introducción a la interfaz Parcelable:

Creo que los que han usado la serialización básicamente conocen esta interfaz, excepto que hay otra Serializable, que también se usa para la serialización, ¡pero Parcelable es más liviana y más rápida! Pero es un poco engorroso de escribir Por supuesto, si usa as, puede usar un complemento para completar la serialización, como: Android Parcelable Code Generator  Por supuesto, aquí todavía le enseñamos cómo implementar esta interfaz ~

Primero debe implementar: los métodos writeToParcel y readFromPacel El método de escritura escribe el objeto en el paquete (parcel) y el método de lectura lee el objeto del paquete Tenga en cuenta que el orden de las propiedades de escritura debe ser el mismo que el orden de lectura

Luego, debe agregar un atributo final estático llamado CREATOR a esta clase . Para cambiar el atributo, debe implementar: Android.os.Parcelable.Creator interface

Luego, debe escribir dos métodos desde la interfaz:  método createFromParcel (fuente de la parcela): realice la función de crear una instancia de JavaBean desde la fuente  newArray (tamaño int): cree una matriz de tipo T y tamaño de longitud, solo un simple retorno de nueva T [tamaño]; (aquí T es la clase Persona)

Finalmente, describeContents() : no sé para qué es esto, ¡simplemente devuelve 0! Ignoralo

——Además , entre los tipos no primitivos , excepto String y CharSequence , el resto necesita un indicador de dirección . Los indicadores de dirección incluyen  entrada , salida y entrada y salida . in significa establecido por el cliente, out significa establecido por el servidor, inout significa que tanto el cliente como el servidor han establecido el valor.


De acuerdo, intentemos escribir algo de código (hay un problema con el tipo personalizado en AS y aún no se ha resuelto, así que usaré Eclipse~):

Ejemplo de código:

Personalice dos tipos de objetos: Persona y Salario, Persona se usa como parámetro para llamar a un Servicio remoto, y Salario se usa como valor de retorno. Luego, lo primero que debe hacer es crear las clases Persona y Salario, y al mismo tiempo necesita implementar la interfaz Parcelable

1.——Servidor

Paso 1 : cree los archivos Person.aidl y Salary.aidl, porque necesitan implementar la interfaz Parcelable, así que solo la siguiente declaración:

Persona.aidl: Persona parcelable; 
Salario.aidl: Salario parcelable;

Paso 2 : Cree la clase Persona y la clase Salario respectivamente, necesita implementar la interfaz Parcelable y reescribir los métodos correspondientes.

PD: Debido a que obtenemos los datos en la colección Map basados ​​en el objeto Person, reescribimos el código hash y los métodos equals en Person.java; ¡la clase Salary no lo necesita!

Persona.java:

paquete com.jay.ejemplo.aidl; 

importar android.os.Parcel; 
importar android.os.Parcelable; 

/** 
 * Creado por Jay el 18/08/2015 0018. 
 */ 
public class Person implements Parcelable{ 

    private Integer id; 
    cadena privada nombre; 

    public Person() {} 

    public Person(Integer id, String name) { 
        this.id = id; 
        este.nombre = nombre; 
    } 

    entero público getId() { 
        devolver id; 
    } 

    public void setId(Integer id) { 
        this.id = id; 
    } 

    public void setName(String nombre) { 
        this.name = nombre; 
    }

    public String getName() { 
        return name; 
    } 


    //Un método que debe implementarse para implementar Parcelable, no sé para qué sirve, solo devuelve 0 directamente 
    @Override 
    public int describeContents() { 
        return 0; 
    } 


    // Escribir datos en Method in Parcel 
    @Override 
    public void writeToParcel(Parcel dest, int flags) { 
        //Escribe los datos contenidos en el objeto en el paquete 
        dest.writeInt(id); 
        dest.writeString(name); 
    } 

    //Debe proporcionar a El atributo final estático denominado CREATOR debe implementar la 
    interfaz //android.os.Parcelable.Creator<T> 
    public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() { 
        //From Parcel Read data y devuelve el objeto Person 
        @Override
        public Person createFromParcel(Parcel source) { 
            return new Person(source. readInt(),source. readString()); 
        } 
        @Override 
        public Person[] newArray(int size) { 
            return new Person[size]; 
        } 
    }; 

    // Porque cuando sacamos elementos de la colección, los obtenemos en función del objeto Person, por lo que es más problemático, 
    //Necesitamos reescribir los métodos hashCode() y equals() 
    @Override 
    public int hashCode() 
    { 
        final int primo = 31; 
        int resultado = 1; 
        resultado = primo * resultado + ((nombre == nulo) ? 0: nombre.hashCode()); 
        devolver resultado; 
    } 
    @Override 
    public boolean equals(Object obj)
    { 
        si (este == obj) 
            devuelve verdadero; 
        si (obj == nulo) 
            devuelve falso; 
        if (getClass() != obj.getClass()) 
            devuelve falso; 
        Persona otra = (Persona) obj; 
        if (nombre == nulo) 
        { 
            if (otro.nombre != nulo) 
                devuelve falso; 
        } 
        else if (!nombre.equals(otro.nombre)) 
            devuelve falso; 
        devolver verdadero; 
    } 
} 
<pre><p><strong>Salario.java</strong>~照葫芦画瓢</p> 

<pre> 
paquete com.jay.example.aidl; 

importar android.os.Parcel;
importar android.os.Parcelable; 

/** 
 * Creado por Jay el 18/8/2015 0018. 
 */ 
public class Salary implements Parcelable { 

    private String type; 
    salario entero privado; 

    public Salary() { 
    } 

    public Salary(tipo de cadena, salario entero) { 
        this.type = type; 
        este.salario = salario; 
    } 

    public String getType() { 
        tipo de retorno; 
    } 

    public Integer getSalary() { 
        return salario; 
    } 

    public void setType(String type) { 
        this.type = type; 
    } 

    public void setSalario(Salario entero) {
        este.salario = salario; 
    } 

    @Override 
    public int describeContents() { 
        return 0; 
    } 

    @Override 
    public void writeToParcel(Parcel dest, int flags) { 
        dest.writeString(type); 
        dest.writeInt(salario); 
    } 

    public static final Parcelable.Creator<Salary> CREATOR = new Parcelable.Creator<Salary>() { 
        //从Parcel中读取数据,返回Person对象
        @Override 
        public Salary createFromParcel(Parcel source) { 
            return new Salary(source. readString(), fuente.readInt()); 
        } 

        @Override 
        public Salary[] newArray(int size) {
            return new Salario[tamaño]; 
        } 
    }; 

    public String toString() { 
        return "Trabajo:" + tipo + " Salario: " + salario; 
    } 
}

Paso 3 : cree un archivo ISalary.aidl y escriba un método simple para obtener información sobre salarios en él:

package com.jay.example.aidl; 
  
import com.jay.example.aidl.Salary;   
import com.jay.example.aidl.Person;   
interface ISalary   
{   
    //Definir un objeto Person como parámetro entrante   
    //Definir métodos en el interfaz Cuando, es necesario formular el modo de transferencia del nuevo parámetro, aquí está el entrante, por lo que hay un en   
    Salario getMsg (en Persona propietario);   
}  

pd: Aquí puede recordar que si está utilizando un tipo de datos personalizado, ¡debe importarlo! ! ! ¡Recordar! ! !

Paso 4: compilación del servicio central: defina una clase SalaryBinder que herede de Stub para implementar las interfaces ISalary e IBinder, defina una colección de mapas que almacene información, rehaga el método onBind y devuelva la instancia de objeto de la clase SalaryBinder.

AidlService.java

paquete com.jay.example.aidl_complexservice;   
  
import java.util.HashMap;   
import java.util.Map;   
import com.jay.example.aidl.ISalary.Stub;   
import com.jay.example.aidl.Person;   
import com. jay.example.aidl.Salary;   
importar android.app.Service;   
importar android.content.Intent;   
importar android.os.IBinder;   
importar android.os.RemoteException;   
  
public class AidlService extends Service {   
  
    private SalaryBinder wageBinder;   
    private static Map< Person, Salary> ss = new HashMap<Person, Salary>();   
    //Inicialice la colección Map, aquí se inicializa en el bloque de código estático, por supuesto, también puede completar la inicialización en el constructor   
    static   
    {  
        ss.put(nueva Persona(1, "Jay"), nuevo Salario("Code Farmer", 2000));   
        ss.put(nueva Persona(2, "GEM"), nuevo Salario("Cantante", 20000)) ;   
        ss.put(nueva Persona(3, "XM"), nuevo Salario("Estudiante", 20));   
        ss.put(nueva Persona(4, "MrWang"), nuevo Salario("Profesor", 2000)) ;   
    }   
      
      
    @Override   
    public void onCreate() {   
        super.onCreate();   
        salarioBinder = new SalaryBinder();   
    }   
      
    @Override   
    public IBinder onBind(Intent intent) {   
        return wageBinder;   
    }   
  
      
    // Misma herencia Stub,  Es decir, implemente tanto la interfaz ISalary como la interfaz IBinder. 
    Clase pública SalaryBinder extends Stub   
    {   
        @Override  
        getMsg de salario público (propietario de la persona) throws RemoteException {   
            return ss.get (propietario);  
        }   
    }   
      
    @Override   
    public void onDestroy() {   
        System.out.println("服务结束!");  
        super.onDestroy();  
    }   
}  

Servicio de registro:

<service android:name=".AidlService">   
    <intent-filter>     
        <action android:name="android.intent.action.AIDLService" />   
        <category android:name="android.intent.category.DEFAULT" />   
    </intent-filter>     
</service>

2——Escribiendo al cliente

Paso 1: copie el archivo AIDL en el lado del servidor, el directorio copiado es el siguiente:

Paso 2 : Escriba un diseño simple y luego implemente el MainActvitiy central para definir un objeto ServiceConnection, reescriba el método correspondiente, similar a los datos ordinarios anteriores, luego bindService, y luego obtenga el objeto Salary en el evento Button click y muéstrelo. ¡afuera!

MainActivity.java

paquete com.jay.ejemplo.aidl_complexclient;  
  
import com.jay.example.aidl.ISalary;  
importar com.jay.ejemplo.aidl.Persona;  
import com.jay.example.aidl.Salario;  
  
importar android.app.Actividad;  
importar android.app.Service;  
importar android.content.ComponentName;  
importar android.content.Intent;  
importar android.content.ServiceConnection;  
importar android.os.Bundle;  
importar android.os.IBinder;  
importar android.os.RemoteException;  
importar android.view.View;  
importar android.view.View.OnClickListener;  
importar android.widget.Button;  
importar android.widget.EditText;  
importar android.widget.TextView;  
  
  
clase pública MainActivity extiende la actividad {   
  
    servicio privado de salario de ISalary;   
    botón privado btnquery;   
    nombre de edición privado de EditText;   
    show de texto de TextView privado;   
    conexión de servicio privada conn = nueva conexión de servicio () {   
          
        @Override   
        public void onServiceDisconnected (nombre del componente) { s   
            alaryService = null;   
        }   
          
        @Override   
        public void onServiceConnected(ComponentName name, IBinder service) {   
            //La devolución es un objeto proxy, ¡debes llamar a este método!   
            wageService = ISalary.Stub.asInterface(service);   
        }   
    };   
      
      
    @Override  
    onCreate void protegido (Paquete de estado de instancia guardado) {   
        super.onCreate (Estado de instancia guardado);  
        setContentView(R.diseño.actividad_principal);  
          
        btnquery = (Botón) findViewById(R.id.btnquery);  
        editname = (EditText) findViewById(R.id.editname);  
        muestratexto = (TextView) findViewById(R.id.textshow);  
          
        Intención = nueva intención ();  
        it.setAction("com.jay.aidl.AIDL_SERVICE");  
        bindService(it, conn, Service.BIND_AUTO_CREATE);  
          
        btnquery.setOnClickListener(nuevo OnClickListener() {            
            @Override   
            public void onClick(Ver v) {   
                probar   
                {  
                    Nombre de cadena = editname.getText().toString();  
                    Salario salario = salarioServicio.getMsg(nueva Persona(1,nombre));  
                    textshow.setText(nombre + salario.toString());  
                }catch(RemoteException e){e.printStackTrace();}   
            }   
        });  
          
    }   
    @Override   
    protected void onDestroy() {   
        super.onDestroy();  
        this.unbindService(conexión);  
    }   
      
}

Ejecute la captura de pantalla:

PD:  El código aquí es el código escrito en Eclipse antes. Hay un problema con el tipo personalizado en Android Studio. No he encontrado una solución por el momento. Si lo sabe, ¡hágamelo saber! ! ! ¡Muchas gracias! ! ! Los problemas que se presentan son los siguientes: 

Descarga de código para dos ejemplos (basado en Eclipse):
1) Usar AIDL para completar una comunicación simple entre procesos
2) Implementación del servicio AIDL para transferir datos complejos


3. Complete la comunicación entre procesos directamente a través de onTransact de Binder

Como se mencionó anteriormente, Android puede completar la comunicación a través del método onTrensact de Binder. Probemos brevemente, o el ejemplo anterior de consulta del nombre basado en el número de serie:

Implementación del servidor :

/** 
 * Creado por Jay el 18/08/2015 0018. 
 */ 
public class IPCService extends Service{ 

    private static final String DESCRIPTOR = "IPCService"; 
    cadena final privada[] nombres = {"B神","艹神","基神","J神","翔神"}; 
    MyBinder privado mBinder = new MyBinder(); 


    clase privada MyBinder extiende Binder { 
        @Override 
        protected boolean onTransact(código int, Datos de paquete, Respuesta de paquete, banderas int) throws RemoteException { 
            switch (código){ 
                case 0x001: { 
                    data.enforceInterface(DESCRIPTOR); 
                    int num = datos.readInt(); 
                    responder.writeNoException();
                    responder.writeString(nombres[num]); 
                    devolver verdadero; 
                } 
            } 
            return super.onTransact(código, datos, respuesta, indicadores); 
        } 
    } 

    @Override 
    public IBinder onBind(Intent Intent) { 
        return mBinder; 
    } 
}

Implementación del cliente :

clase pública MainActivity extiende AppCompatActivity implementa View.OnClickListener{ 

    private EditText edit_num; 
    Botón privado btn_query; 
    Vista de texto privada txt_result; 
    IBinder privado mIBinder; 
    serviceConnection privado PersonConnection = new ServiceConnection() 
    { 
        @Override 
        public void onServiceDisconnected(ComponentName name) 
        { 
            mIBinder = null; 
        } 

        @Override 
        public void onServiceConnected(ComponentName nombre, servicio IBinder) 
        { 
            mIBinder = servicio; 
        } 
    }; 

    @Anular
    onCreate void protegido (Paquete de estado de instancia guardado) { 
        super.onCreate (Estado de instancia guardado); 
        setContentView(R.diseño.actividad_principal); 
        enlazarVistas(); 

        //绑定远程
        Intención de servicio service = new Intent("android.intent.action.IPCService"); 
        servicio.setPackage("com.jay.ipcserver"); 
        bindService(servicio, PersonConnection, BIND_AUTO_CREATE); 
        btn_query.setOnClickListener(esto); 
    } 

    private void bindViews() { 
        edit_num = (EditText) findViewById(R.id.edit_num); 
        btn_query = (Botón) findViewById(R.id.btn_query); 
        txt_result = (TextView) findViewById(R.id.txt_result); 
    }

    @Override 
    public void onClick(View v) { 
        int num = Integer.parseInt(edit_num.getText().toString()); 
        if (mIBinder == null) 
        { 
            Toast.makeText(this, "No conectado al servidor o servidor Eliminado por excepción", Toast.LENGTH_SHORT).show(); 
        } else { 
            android.os.Parcel _data = android.os.Parcel.obtain(); 
            android.os.Parcel _reply = android.os.Parcel.obtain( ); 
            String _result = null; 
            try{ 
                _data.writeInterfaceToken("IPCService"); 
                _data.writeInt(num); 
                mIBinder.transact(0x001, _data, _reply, 0); 
                _reply.readException();
                _resultado = _respuesta.readString(); 
                txt_resultado.setText(_resultado); 
                edit_num.setText(""); 
            }catch (RemoteException e) 
            { 
                e.printStackTrace(); 
            } finalmente 
            { 
                _reply.recycle(); 
                _datos.reciclar(); 
            } 
        } 
} 
    }

Ejecute la captura de pantalla:

El código es relativamente simple, así que no explicaré mucho ~ ¡Solo úsalo y cámbialo tú mismo! PD : se hace referencia al código en: Análisis del marco de Android aidl Binder


4. Algunas cosas a tener en cuenta sobre el servicio después de Android 5.0:

Hoy, cuando inicié el Servicio implícitamente, me encontré con un problema de este tipo. 

 Luego, el programa colapsó tan pronto como se inició, y tomó mucho tiempo descubrir que era culpa de Android 5.0 Resulta que hay una nueva función después de 5.0, es decir: ¡La intención del servicio debe ser explícita  !  Bueno, el servicio no se puede iniciar implícitamente, ¡y la solución es muy simple! Por ejemplo, StartService's:

startService(new Intent(getApplicationContext(), "com.aaa.xxxserver"));  De esta manera, el programa se bloqueará directamente y debe escribirse de la siguiente manera:  startService(new Intent(getApplicationContext(), LoadContactsService.class) );

Si es BindService:  Intent service = new Intent("android.intent.action.AIDLService");  Sobre esta base, agregue el nombre del paquete:  service.setPackage("com.jay.ipcserver");  Eso es todo~

Supongo que te gusta

Origin blog.csdn.net/leyang0910/article/details/131291636
Recomendado
Clasificación