Compétence en service
Introduction à cette section :
Dans cette section, nous continuons à étudier le composant Service. Dans cette section, nous apprendrons une partie de la communication inter-processus AIDL dans Android. Le concept ne va pas en profondeur au niveau du code source. Il suffit de savoir ce que c'est et de savoir comment l'utiliser ! Commencez cette section ~ Cette section correspond au document officiel : Binder
1) Première introduction au mécanisme Binder
1) Que sont IBinder et Liant?
Regardons ce que dit la documentation officielle :
Traduction chinoise :
IBinder est l'interface de base pour objets distants. Il s’agit de la partie centrale du mécanisme d’appel à distance léger conçu pour des performances élevées. Mais il Non seulement pour les appels à distance, mais aussi pour les appels en cours. Cette interface définit le protocole d'interaction avec les objets distants. Mais ne l'implémentez pas directement Cette interface, en revanche, hérite de (extends)Binder.
L'API principale d'IBinder est transact(), et l'API correspondante est Binder.onTransact(). Grâce au premier, vous pouvez Envoie des appels à un objet IBinder distant, ce qui permet à votre objet distant de répondre aux appels entrants. Les API d'IBinder sont toutes Syncronous exécutées, comme transact() jusqu'à ce que la méthode Binder.onTransact() de l'autre partie soit appelée. Je suis revenu plus tard. C'est sans doute le cas lorsque l'appel intervient au sein d'un processus, et lorsqu'il est inter-processus, avec l'aide de IPC, le même effet est obtenu.
Les données envoyées via transact() sont Parcel En plus des données, il contient également. Quelques métadonnées décrivant son contenu. Les métadonnées sont utilisées pour gérer les références aux objets IBinder afin que les tampons puissent être déplacés d'un processus à un autre. Enregistrez ces références lorsque vous passez à un autre processus. Cela garantit que lorsqu'un IBinder est écrit dans un colis et envoyé à un autre processus, Si un autre processus renvoie une référence au même IBinder au processus d'origine, alors le processus d'origine peut recevoir l'envoi. Une référence à cet IBinder. Ce mécanisme permet à IBinder et Binder d'être gérés à travers des processus comme des identifiants uniques.
Le système maintient un pool de threads pour chaque processus afin de stocker les threads interactifs. Ces threads interactifs sont utilisés pour distribuer tous les IPC envoyés par d'autres processus appel. Par exemple : lorsqu'un IPC est envoyé du processus A au processus B, le thread appelant dans A (il ne doit pas être dans le pool de threads) est bloqué. Dans transact(). Un thread dans le pool de threads interactif du processus B reçoit cet appel. Il appelle Binder.onTransact() et renvoie un Parcel comme résultat une fois terminé. Puis le thread en attente dans le processus A L'exécution peut continuer après réception du colis retourné. En fait, l’autre processus ressemble à un fil conducteur du processus en cours, Mais cela ne résulte pas du processus actuel.
Le mécanisme Binder prend également en charge les appels récursifs entre processus. Par exemple, le processus A exécute son propre appel transact() d'IBinder au processus B. Binder et le processus B utilise transact() dans son Binder.onTransact() pour lancer un appel au processus A, puis au processus A. En attendant le retour de l'appel qu'il a émis, il répondra également au transact() du processus B avec Binder.onTransact(). En bref, le résultat de Binder est que nous pensons qu'il n'y a pas de différence entre les appels inter-processus et les appels intra-processus.
Lorsque vous utilisez des objets distants, vous devez souvent vérifier s'ils sont valides. Il existe trois méthodes que vous pouvez utiliser :
- 1 La méthode transact() sera lancée lorsque le processus est terminé. IBinder est localisé et n'existe pas. Une RemoteException se produit.
- 2 Si le processus cible n'existe pas, alors false est renvoyé lors de l'appel de pingBinder().
- 3 Vous pouvez utiliser la méthode linkToDeath() pour enregistrer un IBinder.DeathRecipient auprès d'IBinder. Appelé lorsque le processus représenté par IBinder se termine.
PS : la traduction chinoise est extraite de : Développement Android : qu'est-ce qu'IBinder
D'accord, je suppose que vous pourriez être confus après avoir lu cette liste des choses Dans le brouillard, voici un bref résumé :
IBinder est une interface de communication inter-processus fournie par Android, et nous n'implémentons généralement pas cette interface directement, Au lieu de cela, la communication inter-processus est obtenue en héritant de la classe Binder ! C'est un moyen d'implémenter l'IPC (communication inter-processus) dans Android !
2) Brève analyse du mécanisme Binder
Le mécanisme Binder dans Android se compose d'une série de composants système : Client, Serveur, Service Manager et Binder driver
Le processus d'appel approximatif est le suivant De plus, Service Manager est plus compliqué et ne sera pas étudié en détail ici !
Analyse du processus :
-> Lorsque le Client appelle une méthode dans une interface proxy, la méthode de l'interface proxy will Les paramètres transmis par le client sont regroupés dans des objets Parcel ;
-> Ensuite, l'interface proxy envoie l'objet Parcel au pilote Binder dans le noyau ; ; ;
Ensuite, le serveur lira les données de la requête dans le pilote Binder. Si elles lui sont envoyées, décompressez l'objet Parcel. Traiter et renvoyer les résultats ; PS : Les méthodes définies dans l'interface proxy correspondent aux méthodes définies dans le Serveur. De plus, l'ensemble du processus d'appel est synchrone, c'est-à-dire que pendant le traitement du serveur, le client sera bloqué (verrouillé) ! La définition de l'interface proxy mentionnée ici est AIDL
(Android Interface Description Language) qui sera abordée plus tard !
3) Pourquoi Android utilise-t-il le mécanisme Binder pour établir une communication inter-processus ?
- Fiabilité : sur les appareils mobiles, la communication basée sur client-serveur est généralement utilisée pour établir une communication interne entre Internet et l'appareil. Actuellement, Linux prend en charge IPC, y compris les canaux traditionnels, System V IPC, à savoir file d'attente de messages/mémoire partagée/sémaphore, et seul le socket prend en charge la méthode de communication client-serveur. Le système Android fournit aux développeurs de riches interfaces fonctionnelles pour la communication inter-processus, la lecture multimédia, les capteurs et la transmission sans fil. Ces fonctions sont gérées par différents serveurs. Les développeurs se soucient uniquement d'établir la communication entre le client et le serveur de leur propre application pour utiliser ce service. Il ne fait aucun doute que si un ensemble de protocoles est mis en place à la base pour mettre en œuvre la communication client-serveur, cela augmentera la complexité du système. Lors de la mise en œuvre de cet environnement complexe sur un téléphone mobile aux ressources limitées, la fiabilité est difficile à garantir.
- Performances de transmission : Socket est principalement utilisé pour la communication inter-processus à travers le réseau et la communication inter-processus sur la machine locale, mais l'efficacité de la transmission est faible et la surcharge est élevée. La file d'attente de messages et le pipeline adoptent une méthode de stockage et de transfert, c'est-à-dire que les données sont d'abord copiées de la zone tampon de l'expéditeur vers une zone tampon ouverte par le noyau, puis copiées de la zone tampon du noyau vers la zone tampon du récepteur. Le processus implique au moins deux copies. Bien que la mémoire partagée ne nécessite pas de copie, le contrôle est compliqué. Comparez le nombre de copies de données de différentes méthodes IPC. Mémoire partagée : 0 fois. Classeur : 1 fois. Socket/Pipeline/File d’attente de messages : 2 fois.
- Sécurité : Android est une plateforme ouverte, il est donc important d'assurer la sécurité des applications. Android attribue un UID/PID à chaque application installée, et l'UID du processus peut être utilisé pour identifier l'identité du processus. Traditionnellement, l'utilisateur ne peut renseigner que l'UID/PID dans le paquet de données, ce qui est peu fiable et facilement exploitable par des programmes malveillants. Au lieu de cela, nous demandons au noyau d'ajouter un UID fiable. Par conséquent, pour la fiabilité, la transmission et la sécurité. Android a établi une nouvelle méthode de communication inter-processus. ——Extrait de :Une brève compréhension du mécanisme Binder dans Android
Bien sûr, en tant que développeur junior, nous ne nous soucions pas de ce qui précède, le Binder Le mécanisme nous apporte L'avantage le plus direct est : Nous n'avons pas besoin de nous soucier de la façon dont l'implémentation sous-jacente est implémentée, il nous suffit de personnaliser un fichier d'interface selon les règles de l'AIDL, et ensuite. appelez la méthode dans l'interface pour terminer les deux tâches. Communication inter-processus !
2. Explication détaillée de l'utilisation de l'AIDL
1) Qu'est-ce que l'AIDL ?
Hé, nous avons parlé du terme IPC plus tôt, son nom complet est : communication interprocessus, Étant donné que dans le système Android, chaque application s'exécute dans son propre processus, les données ne peuvent généralement pas être échangées directement entre les processus. Afin de réaliser des processus croisés, Android nous fournit le mécanisme Binder mentionné ci-dessus, et le langage d'interface utilisé par ce mécanisme est : AIDL (Android Interface Definition Language). Sa syntaxe est très simple, et ce). interface Le langage n'est pas vraiment de la programmation Le langage définit simplement l’interface de communication entre deux processus ! Le code Java conforme au protocole de communication est généré par le SDK Android L'outil aidl.exe dans le répertoire platform-tools est généré, et le fichier d'interface correspondant est généré dans le répertoire :gen, généralement l'interface de : Xxx.java ! L'interface contient une classe interne de Stub, qui implémente l'interface IBinder et l'interface de communication personnalisée. Cette classe sera utilisée comme classe de rappel du service distant - elle implémente l'interface IBinder, elle peut donc être utilisée comme valeur de retour de la méthode onBind() du service !
2) AIDL implémente une communication simple entre deux processus
Avant de commencer à écrire le fichier d'interface AIDL, nous devons comprendre quelques notes sur l'écriture d'AIDL :
Notes sur AIDL :
- Le nom de l'interface doit être le même que le nom du fichier AIDL
- Ne pas ajouter d'accès devant l'interface et la méthodeModificateurs d'autorisation : public, privé, protégé, etc., la finale statique ne peut pas être utilisée
- Les types pris en charge par défaut par AIDL incluent Les types de base Java , String, List, Map, CharSequence, tous les autres types sont Une instruction d'importation est requise pour utiliser des types personnalisés comme paramètres ou valeurs de retour, les types personnalisés doivent implémenter l'interface Parcelable. Pour plus de détails, veuillez consulter Passage de types de données complexes
- Les types personnalisés et autres types d'interface générés par AIDL doivent être explicitement importés dans le fichier de description aidl, même dans la classe et la définition. de paquets dans un même paquet.
De plus, si le compilateur que vous utilisez pour écrire aidl est : Eclipse, veuillez noter : Ne créez pas de nouveau fichier directement ! Dans ce cas, le fichier ne peut pas être ouvert et le code ne peut pas être écrit !
① Créez directement un nouveau fichier txt, enregistrez-le au format .aidl après l'écriture, puis copiez-le dans le chemin correspondant
② Parce que aidl est similaire à l'interface, donc directement une nouvelle interface, après avoir écrit le contenu, accédez au fichier java correspondant Modifiez le nom du suffixe du fichier dans le répertoireSi vous utilisez Android Studio, contrairement à Eclipse, si vous créez un fichier AIDL comme Eclipse, vous trouverez Le fichier XXX.java correspondant n'est pas compilé et généré. Pour créer AIDL sous AS, vous devez créer un nouveau dossier aidl dans le répertoire principal puis définir un. Pour un package du même nom que le package aidl, créez enfin un fichier aidl, puis appuyez sur ctrl + f9 pour recompiler, et c'est tout !
Les résultats d'une compilation réussie des deux ci-dessus sont les suivants. Vous pouvez trouver les fichiers AIDL correspondants dans les répertoires correspondants
<🎜. >
1. Serveur :
Étape 1 : Créer un fichier AIDL :
IPerson. aidl
interface IPerson {
String queryPerson(int num);
}
Ouvrons IPerson.java et examinons le code à l'intérieur :
IPerson.java
* Ce fichier est généré automatiquement. NE PAS MODIFIER.
* Fichier d'origine : C:\Code\ASCode\AIDLServer\app\src\main\aidl\com\jay\aidl\IPerson.aidl
*/
package com.jay. aidl;
interface publique IPerson étend android.os.IInterface
{
/** Classe de stub de mise en œuvre IPC côté local. */
classe abstraite statique publique Stub étend android.os.Binder implémente com.jay.aidl. IPerson
{
private static final java.lang.String DESCRIPTOR = "com.jay.aidl.IPerson";
/** Construisez le stub et joignez-le à l'interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Castez un objet IBinder dans une interface com.jay.aidl.IPerson ,
* générant un proxy si nécessaire.
*/
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)&&(dans l'instance de com.jay.aidl.IPerson))) {
return ((com.jay.aidl.IPerson)iin);
}
return new com.jay.aidl .IPerson.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) lance android.os.RemoteException
{
switch (code)
{
cas INTERFACE_TRANSACTION :
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_queryPerson:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.queryPerson(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true ;
}
>
return super.onTransact(code, data, reply, flags);
>
classe statique privée Proxy implémente com.jay.aidl.IPerson
{
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.lang .String queryPerson(int num) lance android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
essayez {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num);
mRemote.transact(Stub.TRANSACTION_queryPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finalement {
_reply.recycle();
_data.recycle();
}
return _result;
>
>
static final int TRANSACTION_queryPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
>
public java.lang.String queryPerson(int num) lance android.os.RemoteException ;
}
Ici, nous ne nous préoccupons que de **asInterface(IBinder)** et de la méthode **queryPerson()** dans l'interface que nous avons définie !
Cette méthode convertira l'objet de type IBinder en IPerson tapez , générez un objet proxy pour renvoyer le résultat si nécessaire !
Nous pouvons sauter le reste et passer à l'étape suivante.
Étape 2 : **Personnalisez notre classe Service et effectuez les opérations suivantes :
1) Héritez de la classe Service et personnalisez également une classe PersonQueryBinder à utiliser pour hériter de la classe IPerson.Stub consiste à implémenter l'interface IPerson et l'interface IBinder
2) Instancier la classe Stub personnalisée et remplacer la méthode onBind de Service, renvoyer un objet classeur !
AIDLService.java
importer android.app.Service;
importer android .content.Intent;
importer android.os.IBinder;
importer android.os.RemoteException;
importer com.jay.aidl.IPerson.Stub;
/ **
* Créé par Jay le 2015/8/18 0018.
*/
classe publique AIDLService étend le service {
private IBinder binder = new PersonQueryBinder();
private String[] noms = {"B神","艹神" ,"Ji Shen ","J Dieu","Xiang Shen"};
requête de chaîne privée (int num)
{
if(num > 0 && num < 6){
return noms[num - 1];
}
return null;
}
@Override
public IBinder onBind(Intent intent) {
return null
}
classe finale privée PersonQueryBinder étend Stub{
@Override
public String queryPerson(int num) throws RemoteException {
return query(num);
}
}
>
Étape 3 :在AndroidManifest.xml文件中注册Service
<intent-filter> ;
<action android:name="android.intent.action.AIDLService" />
<category android:name="android.intent.category.DEFAULT" />
&Lt ; /intent-filter>
</service>
Nous ne fournissons pas d'interface d'activité ici, mais le Service fourni par l'application modifiée peut être appelé par d'autres applications !
2. Le client
copie directement le fichier aidl du serveur, puis nous le complétons directement dans MainActivity et lions le service local
Un peu similaire, le processus est le suivant :
1) Personnalisez la classe PersonConnection pour implémenter l'interface ServiceConnection
2) En utilisant l'objet PersonConnection comme paramètre, appelez bindService pour lier le service distant
bindService(service ,conn,BIND_AUTO_CREATE);
ps : Le troisième paramètre est de définir la création automatique si le service n'est pas démarré
3) Différent du Service local, Lié à ServiceConnection au service distant ne peut pas être directement. Pour obtenir l'objet IBinder renvoyé par la méthode onBind() du Service
, vous ne pouvez renvoyer que l'objet proxy renvoyé par onBind( ) méthode. Vous devez effectuer le traitement suivant :
iPerson = IPerson.Stub.asInterface(service);
Terminez ensuite l'initialisation, les événements des boutons, etc.
Le code spécifique est le suivant :
MainActivity.java
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
importer android.widget.Button;
importer android.widget.EditText;
importer android.widget.TextView;
importer com.jay.aidl.IPerson;
public class MainActivity étend AppCompatActivity implémente View.OnClickListener{
private EditText edit_num;
private Button btn_query;
private TextView txt_name;
private IPerson iPerson ;
privé PersonConnection conn = nouveau PersonConnection();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindViews();
//绑定远程Service
Intent service = new Intent("android.intent.action.AIDLService");
service.setPackage("com.jay.aidlserver");
bindService(service, conn, BIND_AUTO_CREATE);
btn_query.setOnClickListener(this);
}
private void bindViews() {
edit_num = (EditText) findViewById (R.id.edit_num);
btn_query = (Button) findViewById(R.id.btn_query);
txt_name = (TextView) findViewById(R.id.txt_name);
}
@Override
public void onClick(View v) {
String number = edit_num.getText().toString();
int num = Integer.valueOf(number);
try {
txt_name.setText(iPerson.queryPerson(num));
} catch (RemoteException e) {
e.printStackTrace();
}
edit_num.setText("");
}
classe finale privée PersonConnection implémente ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
iPerson = IPerson.Stub.asInterface(service);
}
vide public onServiceDisconnected(ComponentName name) {
iPerson = null;
}
}
}
Ensuite, démarrez AIDLServivce, puis démarrez AIDLClient, entrez le numéro de série de la requête et vous pourrez obtenir le nom correspondant ! Bien entendu, vous pouvez également démarrer AIDLClient directement et obtenir le même effet :
Le rendu est le suivant :
3) Service AIDL qui transmet des données complexes
Dans l'exemple ci-dessus, nous transmettons uniquement des paramètres de type int, puis le serveur renvoie un paramètre de type String, ce qui semble satisfaire Nos besoins fondamentaux, mais dans le développement réel, nous devrons peut-être envisager de transmettre des types de données complexes ! Apprenons ensuite Comment transmettre des données de types de données complexes au serveur ! Avant de commencer, comprenons d’abord l’Interface Parcelable !
——Introduction à l'interface Parcelable :
Je crois que ceux qui ont utilisé la sérialisation connaissent essentiellement cette interface. En plus de celle-ci, il existe une autre sérialisable. , le même est utilisé pour la sérialisation, C'est juste que Parcelable est plus léger et plus rapide ! Mais c'est un peu difficile à écrire. Bien sûr, si vous utilisez as, vous pouvez l'utiliser. Plug-in pour compléter la sérialisation, tel que : Android Parcelable Code GeneratorBien sûr, nous allons vous apprendre ici comment implémenter cette interface~
Tout d'abord, doit être implémenté : les méthodes writeToParcel et readFromPacel La méthode write écrit l'objet dans la parcelle, tandis que la méthode read lit l'objet dans la parcelle. Veuillez noter que l'ordre d'écriture des attributs doit être le même que l'ordre de lecture
Ensuite vous devez ajouter une finale statique< nommée CRÉATEUR à la classe : 🎜>Propriétés
Pour modifier les attributs, vous devez implémenter : android.os.Parcelable.Creatorinterface
puis vous devez écrire deux méthodes dans l'interface : createFromParcel Méthode (Parcel source) : implémente la fonction de création d'une instance JavaBean à partir de la source newArray(int size) : crée un tableau de type T et de longueur size, avec seulement un simple retour new T[size ]; (Voici la classe Person)
Enfin,scribeContents() : Je ne sais pas à quoi cela sert, retournez simplement 0 ! Ignorez-le
- De plus, , types non primitifs , à l'exception de String et CharSequence, le reste sont Un indicateur de direction est requis. Les indicateurs directionnels incluent in, out, et inout. in signifie qu'elle est définie par le client, out signifie qu'elle est définie par le serveur et inout signifie que la valeur est définie à la fois par le client et le serveur.
D'accord, essayons d'écrire du code (il y a un problème avec le type personnalisé ici dans AS, qui n'a pas encore été résolu, je vais donc utiliser Eclipse~) :
Exemple de code :
Personnalisez deux types d'objets : Person et Salary Person est utilisé comme paramètre pour appeler le service distant, et Salary est utilisé comme valeur de retour ! Ensuite, la première chose à faire est de créer les classes Personne et Salaire, et également d'implémenter l'interface Parcelable1. - Serveur
Étape 1 : Créez les fichiers Person.aidl et Salary.aidl, car ils doivent implémenter l'interface Parcelable, donc ce qui suit déclaration :
Person.aidl: parcelable Person; Salary.aidl: parcelable Salary;Étape 2 : Établissez respectivement la classe Personne et la classe Salaire. Vous devez implémenter l'interface Parcelable et réécrire les méthodes correspondantes !
<. 🎜 >PS : Comme nous obtenons plus tard les données de la collection Map basées sur l'objet Person, nous avons réécrit le hashcode et les égaux dans Person.java méthode ; alors que la classe Salary n'en a pas besoin !
Person.java :
importer android.os.Parcel;
importer android.os.Parcelable;
/**
* Créé par Jay le 2015/8/18 0018.
*/
classe publique Personne implémente Parcelable{
identifiant entier privé;
nom de chaîne privé;
personne publique() {}
personne publique(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
//Méthode qui doit être implémentée pour implémenter Parcelable, je ne le fais pas savoir à quoi l'utiliser Oui, retournez simplement 0 directement
@Override
public int décrireContents() {
return 0;
}
//Méthode pour écrire data into Parcel
@Override
public void writeToParcel(Parcel dest, int flags) {
//Écrit les données contenues dans l'objet dans parcel
dest.writeInt(id);
dest . writeString(name);
}
//Un attribut final statique nommé CREATOR doit être fourni. Cet attribut doit implémenter
//android.os.Parcelable.Creator<T>interface<. 🎜> public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
//Lire les données de Parcel et renvoyer l'objet Person
@Override
public Person createFromParcel(Parcel source) {
return new Person(source.readInt(),source.readString());
}
@Override
public Person[] newArray(int size ) {
return new Person[size]; Nous devons remplacer les méthodes hashCode() et equals()
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (o bj == null)
return false;
if ( getClass() != obj .getClass())
return false;
Personne autre = (Personne) obj;
if (name == null)
{
if (other.name != null)
return false;
ante .
<pre>
package com.jay.example.aidl ;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Créé par Jay le 2015/8/18 0018.
*/
classe publique Salaire implémente Parcelable {
type de chaîne privée ;
salaire entier privé ;
Salaire public() {
}
Salaire public (type de chaîne, salaire entier) {
ce.type = type;
this.salary = salary;
}
public String getType() {
return type;
}
public Integer getSalary() {
retourner le salaire ;
}
public void setType(String type) {
this.type = type;
}
public void setSalary(Integer salaire) {
this.salary = salary;
}
@Override
public int describeContents() {
return 0;
}
@ Remplacer
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(type);
dest.writeInt(salary);
}
public static final Parcelable.Creator& LT; Salaire> CREATOR = new Parcelable.Creator<Salary>() {
//从Parcel中读取数据,返回Person对象
@Override
public Salary create From Parcel(Parcel source ) {
retourner un nouveau salaire ( source.readString(), source.readInt());
}
@Override
public Salary[] newArray(int size) {
return new Salary[size];
}
};
public String toString() {
return "Job:" + tapez + " Salaire: " + salaire;
}
}
Étape 3 : Créez un fichier ISalary.aidl dedans Écrivez un méthode simple pour obtenir des informations sur le salaire :
import com.jay.example.aidl.Salary
import com .jay; example.aidl.Person;
interface ISalary
{
//Définir un objet Person comme paramètre entrant
//Lors de la définition d'une méthode dans l'interface, vous devez formuler le mode de livraison du nouveau paramètre, ici Il est transmis, donc il y a un devant
Salary getMsg(in Personowner);
ps :Vous pouvez vous rappeler ici que si vous utilisez un type de données personnalisé, vous devez l'importer ! ! ! Souviens-toi! ! !
Étape 4 : Rédaction du service de base : Définissez une classe SalaryBinder qui hérite de Stub pour implémenter les interfaces ISalary et IBinder ; définissez une collection Map pour stocker les informations ! Réinitialisez la méthode onBind et renvoyez l'instance d'objet de la classe SalaryBinder !
AidlService.java
importer java.util.HashMap
importer java.util.Map
importer com.jay.example.aidl. ISalary.Stub;
importer com.jay.example.aidl.Person;
importer com.jay.example.aidl.Salary;
importer android.app.Service
importer android. os.IBinder;
import android.os.RemoteException;
classe publique AidlService extends Service {
privé SalaryBinder salaireBinder;
privé statique Map< ;Personne, Salaire> HashMap<Person, Salary>();
//Initialisez la collection Map, ici elle est initialisée dans le bloc de code statique, bien sûr vous pouvez également compléter l'initialisation dans le constructeur
static
{
ss.put(new Person(1, "Jay"), new Salary("code farmer", 2000));
ss.put(new Person(2, "GEM"), new Salary("Chanteur" , 20000));
ss.put(new Person(3, "XM"), new Salary("Étudiant", 20));
ss.put(new Person(4 , "MrWang"), new Salary("Enseignant", 2000));
}
super.onCreate();
salaireBinder = new SalaryBinder(); 🎜> public IBinder onBind(Intent intent) {
return salaireBinder
}
//Il hérite également de Stub, c'est-à-dire qu'il implémente à la fois l'interface ISalary et l'interface IBinder.
classe publique SalaryBinder étend Stub
{
@Override
public Salary getMsg(Person owner) lance RemoteException {
return ss.get(owner);
}
}
@Override
public void onDestroy() {
System.out.println("服务结束!");
super.onDestroy();
}
}
注册下Service :
<intent-filter>
<action android:name="android.intent.action.AIDLService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
2——Écriture client
Étape 1 : Copiez le fichier AIDL côté serveur Le répertoire copié est le suivant :
Étape 2 : Écrivez une mise en page simple, puis implémentez le noyau MainActvitiy Définir un objet ServiceConnection et remplacer la méthode correspondante, similaire aux données ordinaires précédentes Ensuite, dans bindService, récupérez l'objet Salary dans l'événement click du bouton et affichez-le !
MainActivity.java
importer com.jay.example.aidl.ISalary ;
importer com.jay.example.aidl.Person ;
importer com.jay.example.aidl.Salary ;
importer android.app.Activity ;
importer android.app.Service ;
importer android.content.ComponentName ;
importer android.content.Intent ;
importer android.content.ServiceConnection ;
importer android.os.Bundle ;
importer android.os.IBinder ;
importer android.os.RemoteException ;
importer android.view.View ;
importer android.view.View.OnClickListener ;
importer android.widget.Button ;
importer android.widget.EditText ;
importer android.widget.TextView ;
classe publique MainActivity étend l'activité {
privée ISalary salaryService ;
requête de bouton privée ;
privé EditText editname ;
exposition de texte TextView privée ;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
s alaryService = null ;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//返回C'est vrai, c'est vrai, c'est vrai !
salaryService = ISalary.Stub.asInterface(service);
}
} ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnquery = (Bouton) findViewById(R.id.btnquery);
editname = (EditText) findViewById(R.id.editname);
textshow = (TextView) findViewById(R.id.textshow);
Intent it = new Intent();
it.setAction("com.jay.aidl.AIDL_SERVICE");
bindService(it, conn, Service.BIND_AUTO_CREATE);
btnquery.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
essayer
{
String name = editname.getText() .toString();
Salaire salary = salaryService.getMsg(new Person(1,name));
textshow.setText(name + salary.toString());
}catch(RemoteException e){ e.printStackTrace();}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
this.unbindService(conn);
}
}
Capture d'écran en cours d'exécution :
PS : Le code ici a été écrit avant d'utiliser le code Eclipse , il y a un problème avec les types personnalisés sous Android Studio. Je n'ai pas encore trouvé de solution. Si quelqu'un le sait, faites-le moi savoir ! ! ! Extrêmement reconnaissant ! ! ! Les problèmes qui se posent sont les suivants :
Téléchargement de code pour deux instances (basé sur Eclipse) :
1) Utiliser AIDL pour réaliser une communication simple entre les processus
2) Implémentation du service AIDL qui transfère des données complexes
3. Communication inter-processus complète directement via onTransact de Binder
Comme mentionné ci-dessus, Android peut terminer la communication via la méthode onTrensact de Binder ici. est Essayons brièvement, en nous basant sur le précédent Exemple de nom de requête de numéro de série :
Implémentation côté serveur :
* Créé par Jay le 2015/8/18 0018.
*/
classe publique IPCService étend le service{
chaîne finale statique privée DESCRIPTOR = "IPCService";
chaîne finale privée[] names = { "B神","艹神","基神","J神","翔神"};
private MyBinder mBinder = new MyBinder();
classe privée MyBinder extends Binder {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) lance RemoteException {
switch (code){
cas 0x001 : {
données. forceInterface(DESCRIPTOR);
int num = data.readInt();
reply.writeNoException();
reply.writeString(names[ num]);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
@Override
public IBinder onBind(In intention de tente) {
retourner mBinder ;
}
}
Implémentation client :
private EditText edit_num;
private Button btn_query;
private TextView txt_result;
privé IBinder mIBinder ;
private ServiceConnection PersonConnection = new ServiceConnection()
{
@Override
public void onServiceDisconnected(ComponentName name)
{
mIBinder = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mIBinder = service;
}
};
@Over rouler
protected void onCreate(Bundle savedInstanceState ) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindViews();
//绑定远程Service
Service d'intention = nouveau Intent("android.intent.action.IPCService");
service.setPackage("com.jay.ipcserver");
bindService(service, PersonConnection, BIND_AUTO_CREATE);
btn_query.setOnClickListener(this) ;
}
private void bindViews() {
edit_num = (EditText) findViewById(R.id.edit_num);
btn_query = (Button) 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, "Le serveur n'est pas connecté ou le serveur est bloqué "Tuer anormalement", toast.Length_short) .show ();
} else {
Android.OS _data = Android.os.parcel.obtain (); );
String _result = null;
_data.writeInt(num);
mIBinder. ly.readException();
_result = _reply.readString();
txt _result.setText(_result);
edit_num.setText("" ; {
_reply.recycle();
_data.recycle() ;
}
Le code est relativement simple, donc je ne vais pas l'expliquer beaucoup ~ utilisez-le simplement et modifiez-le vous-même ! PS : Référence du code : Une brève analyse du framework Android aidl Binder
Quelques éléments à prendre en compte dans le service après Android 5.0 : <🎜. >
Aujourd'hui, lorsque j'ai démarré implicitement le Service, j'ai rencontré un tel problèmehttp://developer.android.com/intl/zh-cn/guide/components/intents-filters.html#TypesPuis le programme s'est écrasé dès son démarrage, et il a fallu beaucoup de temps pour le réparer. Voici ce qui ne va pas avec Android 5.0, Il s'avère qu'il existe une nouvelle fonctionnalité après la version 5.0, à savoir :
L'intention de service doit être explicite ! Eh bien, le Service ne peut pas être démarré implicitement, et la solution est très simple ! Par exemple, StartService :
startService(new Intent(getApplicationContext(), "com.aaa.xxxserver"));Si vous écrivez un programme comme celui-ci, il plantera directement. doit être écrit comme suit : startService(new Intent(getApplicationContext(), LoadContactsService.class));
S'il s'agit de BindService :Intent service = new Intent("android. intent.action.AIDLService");< Sur la base de 🎜>, ajoutez le nom du package : service.setPackage("com.jay.ipcserver");C'est tout~
Document officiel :
Résumé de cette section :
D'accord, c'est la dernière section sur le service. Cette section explique les concepts de base de Binder et comment implémenter la communication inter-processus. Deux manières : communication inter-processus via AIDL et Binder.onTransact() ! Enfin, nous avons également expliqué l'après-Android 5.0 Notez que le service ne peut pas être démarré implicitement ! Ça y est, merci~