Java - Timer und TimerTask

Aus Wikizone
Wechseln zu: Navigation, Suche

Nicht so einfach wie man denkt. Das Problem ist die Ungenauigkeit von Android.


3 Möglichkeiten[Bearbeiten]

package de.webmynet.androidbasics2;

import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Handler.Callback;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class TimerActivity extends Activity {
	TextView tvTimer1, tvTimer2, tvTimer3;
	long starttime = 0;
	
	Timer timer = new Timer();

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_timer);

		tvTimer1 = (TextView) findViewById(R.id.tvTimer1);
		tvTimer2 = (TextView) findViewById(R.id.tvTimer2);
		tvTimer3 = (TextView) findViewById(R.id.tvTimer3);

		Button bTimer = (Button) findViewById(R.id.bTimer);
		bTimer.setText("start");
		bTimer.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				Button bTimer = (Button) v;
				if (bTimer.getText().equals("stop")) {
					timer.cancel();
					timer.purge();
					h2.removeCallbacks(run);
					bTimer.setText("start");
				} else {
					starttime = System.currentTimeMillis();
					timer = new Timer();
					timer.schedule(new firstTask(), 0, 500); // repeat tasks every 500mx..
					timer.schedule(new secondTask(), 0, 500);// starting immediatly
					h2.postDelayed(run, 0);
					bTimer.setText("stop");
				}
			}
		});
	}
	// this posts a message to the main thread from our timertask
	// and updates the textfield
	final Handler h = new Handler(new Callback() {

		@Override
		public boolean handleMessage(Message msg) {
			long millis = System.currentTimeMillis() - starttime;
			int seconds = (int) (millis / 1000);
			int minutes = seconds / 60;
			seconds = seconds % 60;

			tvTimer1.setText(String.format("%d:%02d", minutes, seconds));
			return false;
		}
	});
	
	
	// using a handler to set up runnable thread
	Handler h2 = new Handler();
	Runnable run = new Runnable() {
		@Override
		public void run() {
			long millis = System.currentTimeMillis() - starttime;
			int seconds = (int) (millis / 1000);
			int minutes = seconds / 60;
			seconds = seconds % 60;

			tvTimer3.setText(String.format("%d:%02d", minutes, seconds));

			h2.postDelayed(this, 500);
		}
	};

	// tells handler to send a message
	class firstTask extends TimerTask {

		@Override
		public void run() {
			h.sendEmptyMessage(0);
		}
	};
	/*** Timer 2 tells activity to run on ui thread ***/
	class secondTask extends TimerTask {
		@Override
		public void run() {
			TimerActivity.this.runOnUiThread(new Runnable() {

				@Override
				public void run() {
					long millis = System.currentTimeMillis() - starttime;
					int seconds = (int) (millis / 1000);
					int minutes = seconds / 60;
					seconds = seconds % 60;

					tvTimer2.setText(String.format("%d:%02d", minutes, seconds));
				}
			});
		}
	};
}

beispiel 2[Bearbeiten]

Interessant dazu auch ist folgende Quelle: http://steve.odyfamily.com/?p=12 (2013-03)

Finding clear information on the web about setting up a Timer in Android seems to be a rough task. The general consensus is that the android.os.Handler class should be used instead via the postDelayed() method. In general, this process seems like a valid way to achieve the timer. However, when you want a timer to actually function like a timer, for example, running some process every second, on the second, you quickly see the problems caused by using the postDelayed() method. More information on using the postDelayed() method can be found here: http://android-developers.blogspot.com/2007/11/stitch-in-time.html Using the process described above, if you have a method that you want to call every second (Timer_Tick), and the method itself takes between 100ms – 200ms to complete, after which you do a postDelayed() to run it again in 1000ms, you end up with a timer that runs every 1.2 seconds, and grows progressively out of sync with real time. I suppose you could correct this by determining how long the method took to run, then calling postDelayed with the adjusted time, but that would not be as reliable as an actual timer. Using an actual Timer (java.util.Timer) in conjunction with runOnUiThread() is one way to solve this issue, and below is an example of how to implement it.

public class myActivity extends Activity {

	private Timer myTimer;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		setContentView(R.layout.main);

		myTimer = new Timer();
		myTimer.schedule(new TimerTask() {			
			@Override
			public void run() {
				TimerMethod();
			}
			
		}, 0, 1000);
	}

	private void TimerMethod()
	{
		//This method is called directly by the timer
		//and runs in the same thread as the timer.

		//We call the method that will work with the UI
		//through the runOnUiThread method.
		this.runOnUiThread(Timer_Tick);
	}


	private Runnable Timer_Tick = new Runnable() {
		public void run() {
		
		//This method runs in the same thread as the UI.    	       
		
		//Do something to the UI thread here
	
		}
	};
}

Hopefully this is clear, and can help others that need to address this situation.


Anderer Ansatz....[Bearbeiten]

http://www.nullpointer.at/2011/01/02/timer-und-timertasks-aufgaben-planen-in-java/

Während dein Kollege also im Eiltempo zwischen die Sitzgelegenheit wechselt fasst du den Entschluss nicht so enden zu wollen. Dennoch musst du den Schlaf besiegen, um deinen Job zu behalten. Zum Glück hast du da den PC mit der Überwachungssoftware, auf dem ein JDK installiert ist. Da lässt sich sicher eine Lösung finden.

Beep!

Leider finden sich keine Sounddatein auf dem Rechner, so musst du dir selbst weiterhelfen. Daher erzeugst einen Ton über das awt Toolkit.

import java.awt.Toolkit;
 
public class Beep {
    private static Beep singleton = null;
    private Toolkit defaultToolkit;
 
    public static Beep getInstance() {
        if (singleton == null) {
            singleton = new Beep();
        }
        return singleton;
    }
 
    private Beep() {
        this.defaultToolkit = Toolkit.getDefaultToolkit();
    }
 
    public void beep() {
        this.defaultToolkit.beep();
    }
 
    public static void main(String args[]) {
        Beep b = Beep.getInstance();
        b.beep();
    }
 
}

Jetzt hast du bereits ein Geräusch das dich munter halten kann, dein Kollege schaut dich schon verwundert an, nimmt einen Schluck Kaffee und beschließt dich wieder zu ignorieren. Doch wie schaffst du nun dass das Programm die ganze Schicht lang der Störfaktor ist, den du dir erhoffst? Die Rechte es als regelmäßiges Service laufen zu lassen hast du im Betriebssystem nicht. Aber auch hier gibt es dank Java einen Ausweg:

Timer und TimerTask

Diese wurden geschaffen um zeitgesteuerte Abläufe zu vereinfachen. TimerTask lässt und Runnable, also einen eigenen Thread, implementieren und umfasst alle Geschehnisse die auszuführen sind. Timer hingegen bekommt den TimerTask übergeben und sorgt für dessen Ausführung in einem Verwaltungsthread.

import java.util.Timer;
import java.util.TimerTask;
 
public class KeepMeAwake {
 
    /**
     * Schedules an annoying beep every 5 seconds to keep us awake
     *
     * @param args
     */
    public static void main(String[] args) {
 
        TimerTask action = new TimerTask() {
            public void run() {
                Beep b = Beep.getInstance();
                b.beep();
            }
 
        };
 
        Timer caretaker = new Timer();
        caretaker.schedule(action, 1000, 5000);
 
    }
}

Als erstes wird ein TimerTask erzeugt, indem wir unser vorheriges Beep Objekt nutzen um das Geräusch wiederzugeben. Diesen Task starten wir dann in einem Timer. Dabei beginnen wir mit der Lärmattacke eine Sekunde nach starten des Programms, und lassen uns dann alle 5 Sekunden neu beschallen. Ob das unserem Kollegen gefällt? Egal, uns hält es munter.

Hier noch eine kurze Erklärung zur Timer Klasse:

Die Klasse Timer bietet 4 verschiedene schedule Methoden

schedule(TimerTask task, Date time) – startet einen Task zu einem gewissen Zeitpunkt schedule(TimerTask task, long delay) – startet einen Task nach einer angegebenen Anzahl von Millisekunden schedule(TimerTask task, Date firsttime, long period) – startet einen Task zu einem fixen Zeitpunkt und wiederholt ihn dann nach der zusätzlich angegebenen Anzahl von Millisekunden regelmäßig schedule(TimerTask task, long delay, long period) – startet einen Task nach einer angegebenen Anzahl von Millisekunden und wiederholt ihn dann nach der zusätzlich angegebenen Anzahl von Millisekunden regelmäßig Die letzten beiden Varianten gibt es noch zusätzlich als scheduleAtFixedRate Methoden, die sich um eine exaktere Ausführung bemühen. Dies ist notwendigk so Operationen über einen langen Zeitraum im exakt den gleichbleibenden Intervall ausgeführt werden müssen.

Ein gestarteter Timer kann dann mittels der Methode cancel() wieder beendet werden, wenn er nicht mehr gebraucht wird.

Eine feiner steuerbare Alternative ist der Quartz-Scheduler http://www.quartz-scheduler.org/ Dieser ist freilich Open Source und unter der Apache 2.0 Lizenz nutzbar. Er lässt eine Steuerung von variablen Ausführzeiten und ähnlichem zu und bietet daher ein breiteres Einsatzspektrum. Wenn euch das Thema interessiert, schaut euch diesen doch mal genauer an.

In diesem Sinne – dreht die Lautsprecher voll auf!