English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Explication détaillée de l'exemple de modèle de conception Observateur en Android

L'article présente un exemple de patron Observateur dans la programmation Android. Partageons-le avec vous pour votre référence, voici les détails :

Un, introduction

Le patron Observateur est un patron très utilisé, il est le plus souvent utilisé dans les systèmes GUI, les systèmes abonnement-publication. Ce patron joue un rôle important dans la dé耦isation, dé耦isant l'observé et l'observateur, réduisant ainsi leur dépendance, voire les rendant indépendants. En ce qui concerne les systèmes GUI, l'UI de l'application est variable, en particulier au début, avec les modifications des activités ou des exigences des produits, l'interface de l'application change souvent, mais les logiques d'entreprise changent peu, dans ce cas, le système GUI a besoin d'un mécanisme pour faire face à cette situation, dé耦isant la couche UI des logiques d'entreprise spécifiques, le patron Observateur joue alors un rôle important.

Deux, définition

Définir une relation de dépendance entre objets, de type un à plusieurs, de sorte que chaque fois qu'un objet change d'état, tous les objets qui en dépendent reçoivent une notification et sont automatiquement mis à jour.

Trois, les scénarios d'utilisation

Scénarios de comportements associés, il convient de noter que les comportements associés sont décomposables, et non des relations de "composition".

Scénarios de déclenchement multi-niveaux des événements.

Scénarios d'échange de messages entre systèmes distincts, tels que les mécanismes de traitement des files de messages, des bus d'événements.

Quatre, le schéma de classe UML du patron Observateur

Schéma de classe UML :

Présentation du rôle :

Sujet : Thème abstrait, c'est-à-dire le rôle de l'observé (Observable), le rôle du thème abstrait conserve les références de tous les objets observateurs dans un regroupement, chaque thème peut avoir un nombre quelconque d'observateurs. Le thème abstrait fournit une interface permettant d'ajouter et de supprimer des objets observateurs.

ConcreteSubject : sujet spécifique, ce rôle stocke les états pertinents dans les objets observateurs spécifiques, notifie tous les observateurs enregistrés lorsque le statut interne du sujet change, et le rôle de sujet spécifique est également appelé rôle d'observé spécifique (ConcreteObservable).

Observer : observateur abstrait, ce rôle est la classe abstraite de l'observateur, elle définit une interface de mise à jour, permettant de mettre à jour eux-mêmes lors de la réception de la notification du sujet.

ConcreteObserver : observateur spécifique, ce rôle met en œuvre l'interface d'observateur abstrait définie par le rôle d'observateur abstrait, afin que le statut de l'objet sujet change, le statut de l'objet soit mis à jour.

Cinquième partie : Mise en œuvre simple

Voici un exemple de série télévisée : pour ne pas rater les nouvelles séries télévisées, nous nous abonnons ou suivons cette série télévisée. Lorsque la série télévisée est mise à jour, elle est pushée à nous dès le premier moment. Voici une simple mise en œuvre.

Classe observatrice abstraite :

/**
 * Classe observatrice abstraite, définissant une interface pour tous les observateurs spécifiques, pour mettre à jour eux-mêmes lors de la réception d'une notification
 */
public interface Observer {
  /**
   * Il y a une mise à jour
   * 
   * @param message Message
   */
  public void update(String message);
}

Classe observée abstraite :

/**
 * Classe observée abstraite
 */
public interface Observable {
  /**
   * Pusher un message
   * 
   * @param message Contenu
   */
  void push(String message);
  /**
   * Abonnement
   * 
   * @param observer Abonné
   */
  void register(Observer observer);
}

Classe observatrice spécifique :

/**
 * La classe observatrice spécifique, c'est-à-dire l'abonné
 */
public class User implements Observer {
  @Override
  public void update(String message) {
    System.out.println(name + ", + message + "Mis à jour !");
  }
  // Nom de l'abonné
  private String name;
  public User(String name) {
    this.name = name;
  }
}

Classe observée spécifique :

/**
 * La classe observée spécifique, c'est-à-dire le programme abonné
 */
public class Teleplay implements Observable{
  private List<Observer> list = new ArrayList<Observer>();//Stockage des abonnés
  @Override
  public void push(String message) {
    for(Observer observer:list){
      observer.update(message);
    }
  }
  @Override
  public void register(Observer observer) {
    list.add(observer);
  }
}

Mise en œuvre :

public class Client {
  public static void main(String[] args) {
    //Observé, ici c'est la série télévisée abonnée par l'utilisateur
    Teleplay teleplay = new Teleplay();
    //Observateur, ici c'est l'utilisateur abonné
    User user1 = new User("Xiaoming");
    User user2 = new User("Xiaoguang");
    User user3 = new User("Xiaolan");
    //Abonnement
    teleplay.register(user1);
    teleplay.register(user2);
    teleplay.register(user3);
    //Diffusion de nouvelles nouvelles
    teleplay.push("xxx série télévisée");
  }
}

Résultat :

Xiaoming, la série télévisée xxx a été mise à jour !
Xiaoguang, la série télévisée xxx a été mise à jour !
Xiaolan, la série télévisée xxx a été mise à jour !

D'après le code ci-dessus, il est possible de réaliser un modèle de diffusion de messages un à plusieurs, où les messages de diffusion dépendent de classes abstraites telles que Observer et Observable, tandis que User et Teleplay ne sont pas enchevêtrés, ce qui garantit la flexibilité et l'extensibilité du système d'abonnement.

Six, le modèle d'observateur dans le code source d'Android

1、BaseAdapter

BaseAdapter, je crois que personne ne l'est inconnu, nous en héritons tous dans les adaptateurs de ListView. Passons maintenant à une analyse simple.

Code partie de BaseAdapter :

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
  //Observateur de jeu de données
  private final DataSetObservable mDataSetObservable = new DataSetObservable();
  public boolean hasStableIds() {
    return false;
  }
  public void registerDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.registerObserver(observer);
  }
  public void unregisterDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.unregisterObserver(observer);
  }
  /**
   * Notifie tous les observateurs lorsque le jeu de données change
   */
  public void notifyDataSetChanged() {
    mDataSetObservable.notifyChanged();
  }
}

Regardons la méthode mDataSetObservable.notifyChanged() :

public class DataSetObservable extends Observable<DataSetObserver> {
  /**
   * Appelle {@link DataSetObserver#onChanged} sur chaque observateur.
   * Appelé lorsque le contenu du jeu de données a changé. Le destinataire
   * obtiendra le nouveau contenu la prochaine fois qu'il interroge le jeu de données.
   */
  public void notifyChanged() {
    synchronized(mObservers) {
      // puisque onChanged() est implémenté par l'application, il pourrait faire n'importe quoi, y compris
      // il se retire de lui-même de {@link mObservers} - ce qui pourrait causer des problèmes si
      // un itérateur est utilisé sur l'ArrayList {@link mObservers}.
      // pour éviter de tels problèmes, passez simplement à travers la liste dans l'ordre inverse.
      for (int i = mObservers.size() - 1; i >= 0; i--) {
        mObservers.get(i).onChanged();
      }
    }
  }
}

On peut voir que dans mDataSetObservable.notifyChanged(), il itère sur tous les observateurs et appelle leur onChanged() pour informer les observateurs de ce qui s'est passé.

Alors d'où vient l'observateur ? C'est la méthode setAdapter, voici le code :

@Override
public void setAdapter(ListAdapter adapter) {
    if (mAdapter != null && mDataSetObserver != null) {
      mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
    resetList();
    mRecycler.clear();
    if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) {
      mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
    } else {
      mAdapter = adapter;
    }
    mOldSelectedPosition = INVALID_POSITION;
    mOldSelectedRowId = INVALID_ROW_ID;
    // AbsListView#setAdapter met à jour les états de mode de sélection.
    super.setAdapter(adapter);
    if (mAdapter != null) {
      mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
      mOldItemCount = mItemCount;
      mItemCount = mAdapter.getCount();
      checkFocus();
      mDataSetObserver = new AdapterDataSetObserver();
      mAdapter.registerDataSetObserver(mDataSetObserver);//Enregistrer l'observateur
      ......saut de texte
    }
}

AdapterDataSetObserver est définie dans la classe parente AbsListView de ListView, c'est un observateur de jeu de données, le code :

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
  @Override
  public void onChanged() {
    super.onChanged();
    if (mFastScroller != null) {
      mFastScroller.onSectionsChanged();
    }
  }
  @Override
  public void onInvalidated() {
    super.onInvalidated();
    if (mFastScroller != null) {
      mFastScroller.onSectionsChanged();
    }
  }
}

Il est dérivé de AdapterDataSetObserver, qui est la classe parente AdapterView héritée de AbsListView, le code est le suivant :

class AdapterDataSetObserver extends DataSetObserver {
  private Parcelable mInstanceState = null;
  // Comme mentionné précédemment, l'appel de notifyDataSetChanged() de l'Adapter appelle la méthode onChanged() de tous les observateurs, l'implémentation clé se trouve ici
  @Override
  public void onChanged() {
    mDataChanged = true;
    mOldItemCount = mItemCount;
    // Obtenir le nombre de données dans l'Adapter
    mItemCount = getAdapter().getCount();
    // Détecter le cas où un curseur qui avait été invalidé précédemment a
    // a été reconstituée avec de nouvelles données.
    if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
          && mOldItemCount == 0 && mItemCount > 0) {
      AdapterView.this.onRestoreInstanceState(mInstanceState);
      mInstanceState = null;
    } else {
      rememberSyncState();
    }
    checkFocus();
    // Redéfinir la mise en page des composants AdapterView tels que ListView, GridView, etc.
    requestLayout();
  }
  // Code omis
  public void clearSavedState() {
    mInstanceState = null;
  }
}

Lorsque les données de ListView changent, la fonction notifyDataSetChanged de l'Adapter est appelée, cette fonction appelle à son tour la fonction notifyChanged de DataSetObservable, cette fonction appelle la méthode onChanged de tous les observateurs (AdapterDataSetObserver). C'est un modèle d'observateur !

Septième partie : Résumé

Avantages :

L'observateur et l'observé sont des couplages abstraits, adaptés aux changements d'affaires.

Améliorer la flexibilité et l'extensibilité du système.

Inconvénients :

Lors de l'utilisation du modèle d'observateur, il faut prendre en compte les problèmes d'efficacité de développement et d'efficacité d'exécution. Le programme inclut un observateur, plusieurs observateurs, et les activités de développement et de débogage sont assez complexes. De plus, les notifications de messages en Java sont généralement exécutées en ordre, donc si un observateur est bloqué, cela affectera l'efficacité d'exécution globale. Dans ce cas, une implémentation asynchrone est généralement adoptée.

Les lecteurs intéressés par plus de contenu sur Android peuvent consulter les sujets spéciaux de ce site : "Introduction et tutoriel avancé de développement Android", "Conseils de débogage et solutions de problèmes courants pour Android", "Résumé des utilisations des composants de base d'Android", "Résumé des techniques de vue View d'Android", "Résumé des techniques de layout d'Android" et "Résumé des utilisations des contrôles d'Android"

J'espère que les informations fournies dans cet article seront utiles pour la conception de programmes Android.

Déclaration : Le contenu de cet article est issu du réseau, propriété de ses auteurs respectifs, contribué et téléversé par les utilisateurs d'Internet de manière spontanée. Ce site ne détient pas de droits de propriété, n'a pas été édité par l'homme, et n'assume aucune responsabilité juridique connexe. Si vous trouvez du contenu suspect de violation de droits d'auteur, veuillez envoyer un email à : notice#oldtoolbag.com (veuillez remplacer # par @ lors de l'envoi d'un email pour signaler une violation, et fournir des preuves pertinentes. Une fois vérifié, ce site supprimera immédiatement le contenu suspect de violation de droits d'auteur.)

Vous pourriez aussi aimer