2016-07-08 14 views
2

Ich sah ScheduledExecutorService only loops onceScheduledExecutorService feuert nur einmal

Aber es scheint nicht mein Problem zu beheben.

Ich habe einen benutzerdefinierten Timer und wenn ich start traf es soll einen Rückruf jede Sekunde abzufeuern:

/** 
* Starts the timer. If the timer was already running, this call is ignored. 
*/ 
public void start() 
{ 
    if (_isRunning) 
    { 
     return; 
    } 

    _isRunning = true; 

    // Schedules repeated task that fires each time at the interval given 
    Log.d("Timer", "Starting execution"); 
    _future = _execService.scheduleWithFixedDelay(new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      Log.d("Timer", "Fire"); 

      _elapsedTime += PausableTimer.this._interval; 

      // If time has exceeded duration, stop timer 
      if (_duration > 0 && _elapsedTime >= _duration) 
      { 
       Log.d("Timer", "Finish"); 
       onFinish(); 
       _future.cancel(false); 
      } 
     } 
    }, 0, _interval, TimeUnit.MILLISECONDS); 
} 

Hier ist, wie ich den Timer aufrufen:

_timer = new PausableTimer(1000, PausableTimer.DURATION_INFINITY); 
    _timer.start(); 

Dies schafft ein Timer, der in 1000-Millisekunden-Intervallen für Inifity ausgelöst wird.

Allerdings zeigen meine Protokolle nur einmal feuern. Das Protokoll "Finish" wird nicht angezeigt, daher weiß ich, dass es nicht beendet wurde.

Wissen Sie, warum das nur einmal feuert?

aktualisieren

/** 
* Timer that can play and pause. 
*/ 
public class PausableTimer extends Timer 
{ 
    public static final int DURATION_INFINITY = -1; 

    private Callbacks.VoidCallback _onTick; 
    private Callbacks.VoidCallback _onFinish; 

    private volatile boolean _isRunning = false; 
    private long _interval; 
    private long _elapsedTime; 
    private long _duration; 
    private ScheduledExecutorService _execService = Executors.newSingleThreadScheduledExecutor(); 
    private Future<?> _future = null; 

    /** 
    * Creates a pausable timer. 
    * @param interval The time gap between each tick in millis. 
    * @param duration The period in millis for which the timer should run. 
    * Set it to {@code Timer#DURATION_INFINITY} if the timer has to run indefinitely. 
    */ 
    public PausableTimer(long interval, long duration) 
    { 
     _interval = interval; 
     _duration = duration; 
     _elapsedTime = 0; 
     _isRunning = false; 
    } 




    /// LIFE CYCLE 



    /** 
    * Starts the timer. If the timer was already running, this call is ignored. 
    */ 
    public void start() 
    { 
     if (_isRunning) 
     { 
      Log.d("Timer", "already started running"); 
      return; 
     } 

     _isRunning = true; 

     // Schedules repeated task that fires each time at the interval given 
     Log.d("Timer", "Starting execution"); 
     _future = _execService.scheduleWithFixedDelay(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       Log.d("Timer", "Fire"); 
       onTick(); 

       _elapsedTime += PausableTimer.this._interval; 

       // If time has exceeded duration, stop timer 
       if (_duration > 0 && _elapsedTime >= _duration) 
       { 
        Log.d("Timer", "Finish"); 
        onFinish(); 
        _future.cancel(false); 
       } 
      } 
     }, 0, PausableTimer.this._interval, TimeUnit.MILLISECONDS); 
    } 

    /** 
    * Pauses the timer. 
    */ 
    public void pause() 
    { 
     if(!_isRunning) 
     { 
      return; 
     } 

     _future.cancel(false); 
     _isRunning = false; 
    } 

    /** 
    * Resumes the timer if it was paused, else starts the timer. 
    */ 
    public void resume() 
    { 
     start(); 
    } 

    /** 
    * Called periodically with the _interval set as the delay between subsequent calls. 
    * Fires tick callback if set. 
    */ 
    private void onTick() 
    { 
     if (_onTick != null) 
     { 
      _onTick.callback(); 
     } 
    } 

    /** 
    * Called once the timer has run for the specified _duration. 
    * If the _duration was set as infinity, then this method is never called. 
    * Fires finished callback if set. 
    */ 
    protected void onFinish() 
    { 
     if (_onFinish != null) 
     { 
      _onFinish.callback(); 
     } 

     _isRunning = false; 
    } 

    /** 
    * Stops the timer. If the timer is not running, then this call does nothing. 
    */ 
    public void cancel() 
    { 
     pause(); 
     _elapsedTime = 0; 
    } 




    /// GETTERS 



    /** 
    * @return the elapsed time (in millis) since the start of the timer. 
    */ 
    public long getElapsedTime() 
    { 
     return _elapsedTime; 
    } 

    /** 
    * @return the time remaining (in millis) for the timer to stop. 
    * If the _duration was set to {@code Timer#DURATION_INFINITY}, then -1 is returned. 
    */ 
    public long getRemainingTime() 
    { 
     if (_duration <= PausableTimer.DURATION_INFINITY) 
     { 
      return PausableTimer.DURATION_INFINITY; 
     } 

     return _duration - _elapsedTime; 
    } 






    /// BINDERS 



    /** 
    * @return true if the timer is currently running, and false otherwise. 
    */ 
    public boolean isRunning() 
    { 
     return _isRunning; 
    } 

    /** 
    * Binds onTick callback. 
    */ 
    public void bindOnTick(Callbacks.VoidCallback callback) 
    { 
     _onTick = callback; 
    } 

    /** 
    * Binds onFinish callback. 
    * @param callback 
    */ 
    public void bindOnFinish(Callbacks.VoidCallback callback) 
    { 
     _onFinish = callback; 
    } 
} 

ist hier ein typisches Protokoll. Im Grunde genommen starte ich es, warte 10-15 Sekunden und fange dann wieder an. Es soll jede Sekunde feuern. Es wird jedoch ein- oder zweimal ausgelöst und dann erst wieder ausgelöst.

D/Timer: Reset-Timer

D/Timer: Starten Ausführung

D/Timer: Feuer

D/Timer: Reset-Timer

D/Timer: Beginnend Ausführung

D/Timer: Feuer

D/Timer: Feuer

D/Timer: Reset-Timer

D/Timer: Ab Ausführung

Um zusätzliche klar zu sein, hier einige rufenden Code verwende ich:

private void restartTimer() 
{ 
    if (_timer != null) 
    { 
     _timer.cancel(); 
    } 
    Log.d("Timer", "Reset timer"); 

    _timer = new PausableTimer(1000, PausableTimer.DURATION_INFINITY); 
    _timer.bindOnTick(new Callbacks.VoidCallback() 
    { 
     @Override 
     public void callback() 
     { 
      decrementTimeRemaining(); 
     } 
    }); 
    _timer.start(); 
} 

SOLUTION

Nach Bezifferung darauf hin, dass der onTick() Anruf in meinem run() den Faden gestoppt werden verursacht wurde, löste ich dies durch den onTick() Aufruf an den Hauptthread Dispatching:

 _future = _execService.scheduleWithFixedDelay(new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      Log.d("Timer", "Run begin"); 

      Runnable task = new Runnable() 
      { 
       @Override 
       public void run() 
       { 
        Log.d("Timer", "Main thread tick"); 
        PausableTimer.this.onTick(); 
       } 
      }; 
      Handler mainHandler = new Handler(Looper.getMainLooper()); 
      mainHandler.post(task); 

      _elapsedTime += PausableTimer.this._interval; 
      Log.d("Timer", "Run middle"); 


      // If time has exceeded duration, stop timer 
      if (_duration > 0 && _elapsedTime >= _duration) 
      { 
       Log.d("Timer", "Finish"); 
       onFinish(); 
       _future.cancel(false); 
      } 
     } 
    }, 0, PausableTimer.this._interval, TimeUnit.MILLISECONDS); 
+0

Wenn es nicht beendet ist, dann vielleicht könnte dies die JVM verhindern, einen neuen Thread Laichen. Warum ist es nicht fertig? –

+0

Überprüfen Sie, dass keine Ausnahme in 'onFinish' oder anderen Teilen des runnable-Objekts enthalten ist. –

+0

Danke, ich schaue mir das an, wenn ich wieder im Büro bin! – Aggressor

Antwort

1

Sehr interessante Ursache des Problems (für mich zumindest).

Dieses Problem war mein onTick() Rückruf, ich Brennen wurde:

mir etwas sehr seltsam mit meinem Logs bemerkt. Die run() Protokolle vor den onTick() feuert, und die, die darunter wo nicht:

Log.d("Timer", "Starting execution"); 
    _future = _execService.scheduleWithFixedDelay(new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      Log.d("Timer", "Run begin"); // fires 

      onTick(); // when I remove this, all below logs fire! 

      _elapsedTime += PausableTimer.this._interval; 
      Log.d("Timer", "Run middle"); // didn't fire 
      Log.d("Timer", "Elapsed time " + _elapsedTime); // didn't fire 
      Log.d("Timer", "Duration " + _duration); // didn't fire 

      // If time has exceeded duration, stop timer 
      if (_duration > 0 && _elapsedTime >= _duration) 
      { 
       Log.d("Timer", "Finish"); // didn't fire 
       onFinish(); 
       _future.cancel(false); 
      } 

      Log.d("Timer", "Run End"); // didn't fire 
     } 
    }, 0, PausableTimer.this._interval, TimeUnit.MILLISECONDS); 

Als ich entfernten die onTick() alle gefeuert Protokolle.

Ich vermute, etwas wird mucked up wenn ich versuche, auf den Hauptthread von hier mit der onTick() gehen.

Ich bin mir noch nicht ganz sicher, aber das ist der Grund, dass der Timer nur einmal ausgelöst wurde, der onTick() Anruf muckt es auf.

Ich werde weiter nachforschen und bin offen für Eingaben, die Sie dazu haben könnten.

Lösung ausgelöst, den Rückruf auf dem Hauptthread:

_future = _execService.scheduleWithFixedDelay(new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      Log.d("Timer", "Run begin"); 

      Runnable task = new Runnable() 
      { 
       @Override 
       public void run() 
       { 
        Log.d("Timer", "Main thread tick"); 
        PausableTimer.this.onTick(); 
       } 
      }; 
      Handler mainHandler = new Handler(Looper.getMainLooper()); 
      mainHandler.post(task); 

      _elapsedTime += PausableTimer.this._interval; 
      Log.d("Timer", "Run middle"); 


      // If time has exceeded duration, stop timer 
      if (_duration > 0 && _elapsedTime >= _duration) 
      { 
       Log.d("Timer", "Finish"); 
       onFinish(); 
       _future.cancel(false); 
      } 
     } 
    }, 0, PausableTimer.this._interval, TimeUnit.MILLISECONDS); 
0

1) Sie setzten _isRunning = true; und nie zu false zurückgesetzt;

2) Wo stellst du _duration ein? Wenn dies 0 ist, wird Ihr Timer nie beendet.

3) Sie verwenden _interval und PausableTimer.this._interval: Wollen Sie das tun?

+0

1) Ja, ich wollte dies auf den "Lauf" des Dienstes zu konzentrieren, ist es auf falsch gesetzt auf 2) Seine auf -1 gesetzt, die unendlich ist glaube ich 3) Sie sind richtig, ich auf das explizit auch, um sicherzustellen, aber es hat nichts geändert Der Timer wird immer noch einmal oder zweimal ausgelöst, und feuert nie das 'OnFinish'. – Aggressor

+0

Ich habe meinen Code aktualisiert, um das ganze Paket anzuzeigen – Aggressor