2016-12-06 6 views
0

J'ai un fil sur lequel j'aimerais attendre des événements. Lorsque ce thread est notifié, je voudrais qu'il se réveille et s'il a été plus long que REFRESH_TIME, il invalidera une vue avec un appel à postInvalidate. Sinon, il dormira pendant le temps nécessaire pour tomber le prochain REFRESH_TIME. Pendant ce temps, si d'autres événements tentent de l'avertir de dessiner, un indicateur doit être défini de sorte qu'il soit immédiatement redessiné après la prochaine REFRESH_TIME. Sinon, il attendra indéfiniment jusqu'à la prochaine notification (ou demandé d'arrêter bien sûr, mais ignore cette partie pour plus de simplicité).Fil d'actualisation de la vue Android permettant de ne mettre à jour la vue qu'une fois toutes les N millisecondes et uniquement sur demande

J'essayais de développer ceci dans un verrou synchronisé avec wait et notify et il ne semble pas que je peux utiliser juste cela. WaitForSingleObject avec un timeout vient à l'esprit pour C++, mais en java je n'ai pas encore eu l'occasion de faire quelque chose de similaire. Que faudrait-il utiliser pour cela qui conviendrait le mieux au problème?

Si j'obtiens 100 notifications pendant l'exécution d'une mise à jour, je devrais les traiter comme un seul avis et ajouter juste un autre invalide, en tombant REFRESH_TIME millisecondes après le suivant. En d'autres termes. Les mises à jour ne peuvent pas être manqués.

Voici un code incomplet. Comment pourrais-je le finir?

public class ViewInvalidator extends Thread { 

private View m_view; 

Object lock = new Object(); 
boolean ready = false; 
long m_refreshTimeMilliseconds; 
boolean m_threadRequestedToStop = false; 

public ViewInvalidator(View view, long refreshTimeMilliseconds) { 

    m_view = view; 
    m_refreshTimeMilliseconds = refreshTimeMilliseconds; 
} 

public void postInvalidate() { 

    synchronized(lock) { 

     ready = true; 
     lock.notifyAll(); 
    } 
} 

public void stopThread() { 

    synchronized (lock) { 

     while(!ready) 
      lock.notifyAll(); 
    } 
} 

@Override 
public void run() { 

    long lastPostInvalidate = System.currentTimeMillis(), timeSince; 

    while(true) { 

     if (this.m_threadRequestedToStop) 
      break; 

     try { 

      synchronized (lock) { 

       while (!ready) 
        lock.wait(); 

      } 
     } catch(InterruptedException e) { 

      System.out.println(e.getMessage()); 
      e.printStackTrace(); 
      break; 
     } 

     // someone just woke us up. Check if it was a request to stop. If it was, stop. 
     if (this.m_threadRequestedToStop) 
      break; 

     // if less time than our refresh time, sleep for the difference, then invalidate. Otherwise just invalidate. 
     timeSince = System.currentTimeMillis() - lastPostInvalidate; 
     if(timeSince < m_refreshTimeMilliseconds) 
      try { 
       Thread.sleep(m_refreshTimeMilliseconds - timeSince); 
      } catch(InterruptedException e) {} 

     m_view.postInvalidate(); 
    } 
} 

Merci beaucoup, Mike

+0

Pouvez-vous décrire le but de ce fil de manière plus détaillée? Il n'est pas clair pour l'instant pourquoi vous avez besoin de ce thread plutôt que d'utiliser simplement des blocs ou des temporisations. – Tosh

+0

J'ai un tas de threads chargés de mettre à jour l'interface utilisateur séparément. Je veux conglomérer leurs appels afin qu'ils n'invalident pas inutilement quand ils ne sont pas séparés par REFRESH_TIME. Le thread de mise à jour va le faire et annuler les supplémentaires entre eux, en les traitant comme 1, de sorte que les threads individuels peuvent simplement dire qu'ils ont besoin d'un redraw. – Mike

+0

Je comprends cette partie mais il ne semble pas y avoir une raison pour laquelle vous ne pouvez pas simplement utiliser un booléen pour invalide et le vérifier chaque fois que votre programme serait normalement redessiné – Tosh

Répondre

0
long lastPostInvalidate; 
bool running; 
bool valid; 

public void f(){ 
if(running)if(!valid)synchronized(valid){valid=true;} 
else { 
running = true; 
while(func()){}; 
running = false; 
} 

private bool func() { 
synchronized (valid){valid = false;} 
synchronized (lastPostInvalidate) { 
long next = System.currentTimeMillis(); 
long x = next - timeSincelastPostInvalidate; 
x = m_refreshTimeMilliseconds-x; 
if(x>0)sleep(x); 
m_view.postInvalidate(); 
timeSincelastPostInvalidate = next; 
}return valid; 
} 

oublié cette condition d'utilisation. Ça aurait dû ressembler à ça. Cela dit, le faire de cette façon pourrait potentiellement bloquer votre thread de mise à jour si vous vous attendez à mettre fréquemment à jour la mise à jour, ce qui serait important si vous utilisiez également ces threads pour d'autres choses. Cependant, si vous vous attendez à des mises à jour fréquentes, vous pouvez aussi bien utiliser le thread que vous avez prévu à l'origine, mais ne pas le désactiver et vérifier chaque intervalle s'il doit être lancé ou non.