przykład jak można to zrobić, można tez użyć Timer w moim przykładzie jest to zwyczajny Handler:
kod usługi, która niezależnie od Aktywności odlicza czas i przesyła do aktywności co ~1s ale czas jest liczony z dokładnością co do 1 ms:
public class Timer extends Service {
public static final int REGISTER_CLIENT = 1;
public static final int REMOVE_CLIENT = 2;
public static final int CURRENT_TIME = 3;
public static final int START_TIMER = 4;
public static final int STOP_TIMER = 5;
private final ArrayList<Messenger> mClients = new ArrayList<Messenger>();
private final Messenger mMessenger = new Messenger(new reciveHandler());
private Repeat repeatTask;
@Override
public IBinder onBind(Intent arg0) {
return mMessenger.getBinder();
}
@Override
public void onCreate() {
super.onCreate();
repeatTask = new Repeat(execute);
Log.d("Service", "Timer Service Start");
}
@Override
public void onDestroy() {
super.onDestroy();
if (repeatTask.running()) repeatTask.stop();
repeatTask = null;
Log.d("Service", "Timer Service Stop");
}
private void sendHandler(Message message) {
if (!mClients.isEmpty()) {
message.replyTo = mMessenger;
for (int i = (mClients.size() - 1); i >= 0; i--) {
try {
mClients.get(i).send(message);
} catch (RemoteException e) {
Log.i("Info", "Remote Exception");
mClients.remove(i);
}
}
}
}
private class reciveHandler extends Handler {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case REGISTER_CLIENT:
mClients.add(message.replyTo);
break;
case REMOVE_CLIENT:
mClients.remove(message.replyTo);
break;
case START_TIMER:
repeatTask.start(0, 1, 0, Repeat.SECUND);
break;
case STOP_TIMER:
repeatTask.stop();
break;
default:
super.handleMessage(message);
}
}
}
private final Repeat.execute execute = new Repeat.execute() {
public boolean onExecute(long runningTime) {
sendHandler(Message.obtain(null, CURRENT_TIME, runningTime));
return true;
}
public void onFinish(long runningTime) {
sendHandler(Message.obtain(null, CURRENT_TIME, runningTime));
}
};
}
kod aktywności, aktualny czas otrzymuje z Usługi i zasadniczo służy jedynie do sterowania u wyświetlania czasu, żeby uruchomić stoper trzeba w dowolnym miejscu wykonać:
sendHandler(Message.obtain(null, Timer.START_TIMER));
żeby zatrzymać stoper należy wykonać:
sendHandler(Message.obtain(null, Timer.STOP_TIMER));
public class Stoper extends Activity {
private static final String TIME_FORMAT = "HH:mm:s,S";
private Messenger mService = null;
private final Messenger mMessenger = new Messenger(new reciveHandler());
private final SimpleDateFormat dateFormat = new SimpleDateFormat();
private final Date currentDate = new Date();
private boolean isBound = false;
private Intent serviceIntent = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(getServiceIntent());
}
@Override
protected void onResume() {
super.onResume();
doBindService();
}
@Override
protected void onPause() {
doUnbindService();
if (isFinishing()) stopService(getServiceIntent());
super.onPause();
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
sendHandler(Message.obtain(null, Timer.REGISTER_CLIENT));
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
private void doBindService() {
bindService(getServiceIntent(), mConnection, 0);
isBound = true;
}
private void doUnbindService() {
if (isBound) {
if (mService != null) {
sendHandler(Message.obtain(null, Timer.REMOVE_CLIENT));
}
unbindService(mConnection);
isBound = false;
}
}
private Intent getServiceIntent() {
if (serviceIntent == null) serviceIntent = new Intent(Stoper.this, Timer.class);
return serviceIntent;
}
private void sendHandler(Message message) {
try {
message.replyTo = mMessenger;
mService.send(message);
} catch (RemoteException e) {
Log.i("Info", "Remote Exception");
}
}
private class reciveHandler extends Handler {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case Timer.CURRENT_TIME:
currentDate.setTime((Long) message.obj);
//tutaj aktywność otrzymuje czas i może go wyświetlić, dla potrze przykładu wyświetlane jest w konsoli
System.out.println("Result: " + formatDate(currentDate, TIME_FORMAT));
break;
default:
super.handleMessage(message);
}
}
}
private String formatDate(Date currentDate, final String format) {
dateFormat.applyLocalizedPattern(format);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat.format(currentDate);
}
}
oraz klasa pomocnicza, odpowiedzialna za liczenie czasu dla zachowania czytelności całego projektu jest osobnym elementem programu:
public class Repeat implements Runnable {
public static final int MILLIS = 1;
public static final int SECUND = 1000;
public static final int MINUTE = 60000;
private static enum Running {YES, NO}
private final Handler mHandler = new Handler();
private volatile Running mRunning = Running.NO;
private Repeat.execute mCallBack = null;
private int mCycleTime;
private int mEndTime;
private long startTime = 0;
public Repeat(Repeat.execute callBack) {
mCallBack = callBack;
}
public interface execute {
boolean onExecute(long runningTime);
void onFinish(long runningTime);
}
public boolean start(final int beginTime, final int cycleTime, final int endTime, final int unit) {
if (mRunning == Running.YES) return false;
mCycleTime = cycleTime * unit;
mEndTime = endTime * unit;
startTime = System.currentTimeMillis() + beginTime * unit;
if (mHandler.postDelayed(this, beginTime * unit)) {
mRunning = Running.YES;
return true;
}
return false;
}
public void stop() {
mRunning = Running.NO;
mHandler.removeCallbacks(this);
}
public boolean running() {
return mRunning == Running.YES;
}
public void run() {
if (mCallBack != null) {
final long currentTime = System.currentTimeMillis() - startTime;
if (mRunning == Running.YES && (mCallBack.onExecute(currentTime) || currentTime < mEndTime)) {
mHandler.postDelayed(this, mCycleTime);
} else {
mCallBack.onFinish(currentTime);
}
}
}
}