2010-06-29 4 views
2

Je suis curieux de la sécurité de cette méthode que j'ai envisagé d'utiliser pour passer des événements tactiles dans mon application Android (et tester ma compréhension de la concurrence en Java). Voici les bases:Une référence volatile est-elle un moyen sûr de passer des MotionEvents entre threads?

J'ai un SurfaceView branché à SurfaceHolder.Callback pour obtenir des événements d'entrée utilisateur, principalement le rappel onTouchEvent. Après que la méthode onTouchEvent est invoquée, je vois si event.getAction() == MotionEvent.ACTION_UP et, si c'est le cas, appelle une méthode que j'ai nommée postTouchEvent qui est une méthode membre de mon thread d'application qui met à jour l'état de l'application et dessine dans le canevas.

SurfaceView

@Override 
public boolean onTouchEvent(final MotionEvent event) 
{ 
    mAppThread.postTouchEvent(event); 
} 

AppThread

private volatile MotionEvent mInputEvent; 

public void postTouchEvent(final MotionEvent event) 
{ 
    this.mInputEvent = event; 
} 

@Override 
public void run() 
{ 
    // while loop ... 

    if (this.mInputEvent != null) 
    { 
     final MotionEvent inputEvent = this.mInputEvent; 
     this.mInputEvent == null; 

     // update state based on inputEvent (not this.mInputEvent) 
    } 

    // draw to canvas 
} 

Maintenant, je comprends qu'il est certainement pas atomique, mais puisque je traite comme immuable après l'avoir reçu du cadre, ne ce travail? (Au lieu de synchroniser la méthode post et l'instruction if, que je n'ai aucun problème à faire, mais je demande à apprendre.)

Voici mes pensées. Je sais que je vais avoir une référence valide à l'objet mais je ne sais pas dans quel état je verrai réellement l'objet. Tout en testant tout fonctionne très bien mais je suis conscient de la façon dont les exceptions de threading peu communes peuvent être, même si quelque chose est cassé.

Aussi, je peux voir un problème avec ceci: Si un autre MotionEvent vient cependant, il est possible que inputEvent dans la méthode run() sera mis à un événement différent de celui qui a été référencé lors this.mInputEvent != null a été vérifiée, mais vraiment ISN pas un problème.

Alors, y a-t-il quelque chose qui me manque ou, à mes fins, cela devrait-il être acceptable?

Répondre

15

Non, ce n'est pas sûr, mais pas pour la raison que vous pourriez vous attendre.

ViewRoot.java, line 1841. C'est le code qui distribue vos MotionEvents dans la hiérarchie de vue. La ligne 1841 fait partie d'un bloc finally qui appelle recycle() sur le MotionEvent qui vient d'être distribué. Les événements MotionEvents ne doivent pas être collectés comme la plupart des objets, ils sont regroupés et recyclés pour éviter l'allocation de mémoire inutile et la récupération de place lors de l'envoi d'événements. Le recyclage d'un MotionEvent le renvoie au pool d'objets pour être réutilisé ultérieurement lorsqu'un nouveau MotionEvent est nécessaire. Après l'appel de recycle(), un événement MotionEvent doit être considéré comme invalide.

Votre exemple de code peut finir par lire un objet MotionEvent qui a été réutilisé par le framework et qui contient maintenant des données complètement différentes.

Si vous envisagez de vous accrocher à un événement MotionEvent après le retour de onTouchEvent, clonez-le en utilisant MotionEvent.obtain(event). La méthode obtain() statique renvoie un nouveau MotionEvent du pool d'objets avec le même contenu.

Vous devez appeler recycle() lorsque vous avez terminé avec un événement MotionEvent que vous obtain() vous avez édité pour le renvoyer au pool. Ce n'est pas une grosse affaire si vous oubliez cette étape, elle deviendra la poubelle Java régulière et le framework créera de nouveaux MotionEvents si nécessaire. Cependant, MotionEvents peut être envoyé très rapidement par le système au point où la coopération avec cette optimisation peut faire une différence non triviale de performance sur certains appareils.

+0

C'est exactement ce que je cherchais à apprendre. Merci d'avoir pris le temps de fournir une réponse détaillée et une solution suggérée. – Guzba

+1

Vous êtes les bienvenus! :) – adamp

+0

Sauvé ma vie :) – Gilian

1

Je pense que c'est "sûr", modulo le point que vous avez déjà identifié sur les événements perdus.

Il existe une relation "arrive avant" pour une action d'écriture sur une action volatile et une action de lecture suivante. Ceci, combiné avec les relations "arrive avant" inhérentes aux actions au sein d'un même thread, devrait signifier que le thread de votre application verra le vrai état de l'objet événement au moment où sa référence a été écrite dans le volatile.

Bien sûr, si le thread d'écouteur d'événement modifie l'état de l'objet d'événement après la mise à jour de la référence, il n'existe aucune garantie que le thread d'application les verra. Idem, si un troisième thread met à jour l'objet événement.

Questions connexes