如何从 IntentService 中管理 CountdownTimer?
How do you Manage a CountdownTimer from within an IntentService?
我正在使用 IntentService 实现番茄钟计时器,我似乎在努力管理 IntentService 中的计时器。我现在有以下内容:
代码:
package com.nursson.pomodorotimer.service;
import android.app.IntentService;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.Looper;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.nursson.pomodorotimer.model.Pomodoro;
/**
* An {@link IntentService} subclass for handling asynchronous task requests in
* a service on a separate handler thread.
* <p/>
* helper methods.
*/
public class PomodoroService extends IntentService {
public static final String ACTION_CANCELLED = "com.nursson.pomodorotimer.service.pomodoro.action.CANCELLED";
public static final String ACTION_COMPLETE = "com.nursson.pomodorotimer.service.pomodoro.action.COMPLETE";
public static final String ACTION_START = "com.nursson.pomodorotimer.service.pomodoro.action.START";
public static final String ACTION_STOP = "com.nursson.pomodorotimer.service.pomodoro.action.STOP";
public static final String ACTION_TICK = "com.nursson.pomodorotimer.service.pomodoro.action.TICK";
public static final String EXTRA_TIME_REMAINING = "com.nursson.pomodorotimer.service.pomodoro.param.TIME_REMAINING";
private boolean started;
private CountDownTimer countDownTimer = new CountDownTimer(10000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
Intent tickIntent = new Intent(ACTION_TICK);
tickIntent.putExtra(EXTRA_TIME_REMAINING, millisUntilFinished);
LocalBroadcastManager.getInstance(PomodoroService.this).sendBroadcast(tickIntent);
Log.i("TIMER", "ticking...");
}
@Override
public void onFinish() {
started = false;
Intent tickIntent = new Intent(ACTION_COMPLETE);
LocalBroadcastManager.getInstance(PomodoroService.this).sendBroadcast(tickIntent);
Log.i("TIMER", "countdown Finished()");
Looper.myLooper().quit();
}
};
public PomodoroService() {
super("PomodoroService");
}
@Override
public void onCreate() {
super.onCreate();
Log.i("TIMER", "onCreate()");
}
@Override
public void onDestroy() {
super.onDestroy();
countDownTimer.cancel();
Log.i("TIMER", "onDestroy()");
}
@Override
public void onHandleIntent(Intent intent) {
if (intent == null) {
return;
}
switch (intent.getAction()) {
case ACTION_START:
handleStartAction();
break;
case ACTION_STOP:
handleStopAction();
break;
}
Looper.loop();
}
private void handleStopAction() {
Log.i("TIMER", "handleStopAction(): " + started);
if (started) {
Log.i("TIMER", "handleStopAction() - Cancel Timer");
started = false;
countDownTimer.cancel();
Intent tickIntent = new Intent(ACTION_CANCELLED);
LocalBroadcastManager.getInstance(PomodoroService.this).sendBroadcast(tickIntent);
}
}
private void handleStartAction() {
Log.i("TIMER", "handleStartAction()" + started);
if (!started) {
Log.i("TIMER", "handleStartAction() - Starting Timer");
started = true;
countDownTimer.start();
}
}
boolean isStarted() {
return started;
}
CountDownTimer getCountDownTimer() {
return countDownTimer;
}
}
代码中发生了什么
所以这基本上是一个包含 CountdownTimer 的 IntentService,用于向活动广播消息。它还使用 Looper.loop();
和 Looper.myLooper().quit();
来尝试和管理生命周期。如果我不包括它,那么 onDestroy()
被调用并且 CountdownTimer 运行直到结束。我希望 onDestroy()
在计时器结束后被调用
我得到的异常是当代码到达 Looper.myLooper().quit();
时,这是下面的堆栈跟踪:
08-29 02:27:38.855 3337-3337/com.nursson.pomodorotimer E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.nursson.pomodorotimer, PID: 3337
java.lang.IllegalStateException: Main thread not allowed to quit.
at android.os.MessageQueue.quit(MessageQueue.java:415)
at android.os.Looper.quit(Looper.java:228)
at com.nursson.pomodorotimer.service.PomodoroService$override.onFinish(PomodoroService.java:44)
at com.nursson.pomodorotimer.service.PomodoroService$override.access$dispatch(PomodoroService.java)
at com.nursson.pomodorotimer.service.PomodoroService.onFinish(PomodoroService.java:0)
at android.os.CountDownTimer.handleMessage(CountDownTimer.java:127)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
我想要发生的事情
我希望能够使用 IntentService 启动和停止 CountDownTimer,并在执行此操作时成为 "good citizen"。
最终目标是拥有一个计时器,该计时器可以在方向改变期间持续,并且可以在 activity 终止时继续 运行。
到目前为止我尝试了什么:
- 正在删除
Looper.myLooper().quit();
。然后 onDestroy()
永远不会在 CountDownTimer 完成时被调用。
- 删除所有
Looper
引用。然后 onDestroy()
被过早调用。计时器工作,但我无法获得对 CountDownTimer 的引用来取消它。
我想到的:
- 有没有更适合我想做的事情的 CountDownTimer 替代品?也许是一个 AlarmManager?
- 我应该使用 Service 而不是 IntentService,这样可以更轻松地处理 CountDownTimer 吗?
我见过的 Looper
的所有示例都使用此模式
Looper.prepare();
// create a handler
Looper.loop();
它为没有消息循环的线程创建并 运行s 一个消息循环,处理程序用于与循环交互。
现在在你的代码中我既没有看到 prepare
也没有看到 Handler
,所以首先你没有遵循典型的模式并且滥用了 Looper.loop()
的阻塞字符来防止onHandleIntent
从终止。
在 onFinish
中调用 Looper.myLooper().quit()
无效,因为 CountDownTimer
方法似乎在主线程上 运行,因此您试图退出主线程的消息循环.好像不太喜欢
如果您只需要阻塞 onHandleIndent
直到 onFinish
被执行,为什么不使用普通的 Java 线程模式之一?喜欢
private final Lock lock = new ReentrantLock();
private final Condition waitForFinish = lock.newCondition();
@Override
public void onHandleIntent(Intent intent) {
if (intent == null) {
return;
}
switch (intent.getAction()) {
case ACTION_START:
handleStartAction();
break;
case ACTION_STOP:
handleStopAction();
break;
}
lock.lock();
try {
while (started) {
waitForFinish.await();
}
} catch (InterruptedException ie) {
// log exceptions
} finally {
lock.unlock();
}
}
和
@Override
public void onFinish() {
started = false;
Intent tickIntent = new Intent(ACTION_COMPLETE);
LocalBroadcastManager.getInstance(PomodoroService.this).sendBroadcast(tickIntent);
Log.i("TIMER", "countdown Finished()");
lock.lock();
try {
waitForFinish.signal();
} finally {
lock.unlock();
}
}
缺点 – IntentService 在单个工作线程中处理请求,因此只要它在 onHandleIntent
中阻塞,它就不会接受新请求,例如停止计时器的请求(最好检查一下而不是相信我 - 我这里没有 android SDK 来证明它)。
另一种解决方案是将阻塞调用放入 onDestroy
- 它将释放 onHandleIntent
用于新请求并阻止服务在计时器完成之前完成。
或者使用另一种类型的服务——绑定或非绑定,它在停止工作后不会自行销毁。不过它们也有缺点——两者都不是为了在主线程之外工作,这就是 IntentService 的目的。
我正在使用 IntentService 实现番茄钟计时器,我似乎在努力管理 IntentService 中的计时器。我现在有以下内容:
代码:
package com.nursson.pomodorotimer.service;
import android.app.IntentService;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.Looper;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.nursson.pomodorotimer.model.Pomodoro;
/**
* An {@link IntentService} subclass for handling asynchronous task requests in
* a service on a separate handler thread.
* <p/>
* helper methods.
*/
public class PomodoroService extends IntentService {
public static final String ACTION_CANCELLED = "com.nursson.pomodorotimer.service.pomodoro.action.CANCELLED";
public static final String ACTION_COMPLETE = "com.nursson.pomodorotimer.service.pomodoro.action.COMPLETE";
public static final String ACTION_START = "com.nursson.pomodorotimer.service.pomodoro.action.START";
public static final String ACTION_STOP = "com.nursson.pomodorotimer.service.pomodoro.action.STOP";
public static final String ACTION_TICK = "com.nursson.pomodorotimer.service.pomodoro.action.TICK";
public static final String EXTRA_TIME_REMAINING = "com.nursson.pomodorotimer.service.pomodoro.param.TIME_REMAINING";
private boolean started;
private CountDownTimer countDownTimer = new CountDownTimer(10000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
Intent tickIntent = new Intent(ACTION_TICK);
tickIntent.putExtra(EXTRA_TIME_REMAINING, millisUntilFinished);
LocalBroadcastManager.getInstance(PomodoroService.this).sendBroadcast(tickIntent);
Log.i("TIMER", "ticking...");
}
@Override
public void onFinish() {
started = false;
Intent tickIntent = new Intent(ACTION_COMPLETE);
LocalBroadcastManager.getInstance(PomodoroService.this).sendBroadcast(tickIntent);
Log.i("TIMER", "countdown Finished()");
Looper.myLooper().quit();
}
};
public PomodoroService() {
super("PomodoroService");
}
@Override
public void onCreate() {
super.onCreate();
Log.i("TIMER", "onCreate()");
}
@Override
public void onDestroy() {
super.onDestroy();
countDownTimer.cancel();
Log.i("TIMER", "onDestroy()");
}
@Override
public void onHandleIntent(Intent intent) {
if (intent == null) {
return;
}
switch (intent.getAction()) {
case ACTION_START:
handleStartAction();
break;
case ACTION_STOP:
handleStopAction();
break;
}
Looper.loop();
}
private void handleStopAction() {
Log.i("TIMER", "handleStopAction(): " + started);
if (started) {
Log.i("TIMER", "handleStopAction() - Cancel Timer");
started = false;
countDownTimer.cancel();
Intent tickIntent = new Intent(ACTION_CANCELLED);
LocalBroadcastManager.getInstance(PomodoroService.this).sendBroadcast(tickIntent);
}
}
private void handleStartAction() {
Log.i("TIMER", "handleStartAction()" + started);
if (!started) {
Log.i("TIMER", "handleStartAction() - Starting Timer");
started = true;
countDownTimer.start();
}
}
boolean isStarted() {
return started;
}
CountDownTimer getCountDownTimer() {
return countDownTimer;
}
}
代码中发生了什么
所以这基本上是一个包含 CountdownTimer 的 IntentService,用于向活动广播消息。它还使用 Looper.loop();
和 Looper.myLooper().quit();
来尝试和管理生命周期。如果我不包括它,那么 onDestroy()
被调用并且 CountdownTimer 运行直到结束。我希望 onDestroy()
在计时器结束后被调用
我得到的异常是当代码到达 Looper.myLooper().quit();
时,这是下面的堆栈跟踪:
08-29 02:27:38.855 3337-3337/com.nursson.pomodorotimer E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.nursson.pomodorotimer, PID: 3337
java.lang.IllegalStateException: Main thread not allowed to quit.
at android.os.MessageQueue.quit(MessageQueue.java:415)
at android.os.Looper.quit(Looper.java:228)
at com.nursson.pomodorotimer.service.PomodoroService$override.onFinish(PomodoroService.java:44)
at com.nursson.pomodorotimer.service.PomodoroService$override.access$dispatch(PomodoroService.java)
at com.nursson.pomodorotimer.service.PomodoroService.onFinish(PomodoroService.java:0)
at android.os.CountDownTimer.handleMessage(CountDownTimer.java:127)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
我想要发生的事情
我希望能够使用 IntentService 启动和停止 CountDownTimer,并在执行此操作时成为 "good citizen"。
最终目标是拥有一个计时器,该计时器可以在方向改变期间持续,并且可以在 activity 终止时继续 运行。
到目前为止我尝试了什么:
- 正在删除
Looper.myLooper().quit();
。然后onDestroy()
永远不会在 CountDownTimer 完成时被调用。 - 删除所有
Looper
引用。然后onDestroy()
被过早调用。计时器工作,但我无法获得对 CountDownTimer 的引用来取消它。
我想到的:
- 有没有更适合我想做的事情的 CountDownTimer 替代品?也许是一个 AlarmManager?
- 我应该使用 Service 而不是 IntentService,这样可以更轻松地处理 CountDownTimer 吗?
我见过的 Looper
的所有示例都使用此模式
Looper.prepare();
// create a handler
Looper.loop();
它为没有消息循环的线程创建并 运行s 一个消息循环,处理程序用于与循环交互。
现在在你的代码中我既没有看到 prepare
也没有看到 Handler
,所以首先你没有遵循典型的模式并且滥用了 Looper.loop()
的阻塞字符来防止onHandleIntent
从终止。
在 onFinish
中调用 Looper.myLooper().quit()
无效,因为 CountDownTimer
方法似乎在主线程上 运行,因此您试图退出主线程的消息循环.好像不太喜欢
如果您只需要阻塞 onHandleIndent
直到 onFinish
被执行,为什么不使用普通的 Java 线程模式之一?喜欢
private final Lock lock = new ReentrantLock();
private final Condition waitForFinish = lock.newCondition();
@Override
public void onHandleIntent(Intent intent) {
if (intent == null) {
return;
}
switch (intent.getAction()) {
case ACTION_START:
handleStartAction();
break;
case ACTION_STOP:
handleStopAction();
break;
}
lock.lock();
try {
while (started) {
waitForFinish.await();
}
} catch (InterruptedException ie) {
// log exceptions
} finally {
lock.unlock();
}
}
和
@Override
public void onFinish() {
started = false;
Intent tickIntent = new Intent(ACTION_COMPLETE);
LocalBroadcastManager.getInstance(PomodoroService.this).sendBroadcast(tickIntent);
Log.i("TIMER", "countdown Finished()");
lock.lock();
try {
waitForFinish.signal();
} finally {
lock.unlock();
}
}
缺点 – IntentService 在单个工作线程中处理请求,因此只要它在 onHandleIntent
中阻塞,它就不会接受新请求,例如停止计时器的请求(最好检查一下而不是相信我 - 我这里没有 android SDK 来证明它)。
另一种解决方案是将阻塞调用放入 onDestroy
- 它将释放 onHandleIntent
用于新请求并阻止服务在计时器完成之前完成。
或者使用另一种类型的服务——绑定或非绑定,它在停止工作后不会自行销毁。不过它们也有缺点——两者都不是为了在主线程之外工作,这就是 IntentService 的目的。