java - 如何从 IntentService 中管理倒计时计时器?

标签 java android multithreading asynchronous android-service

我正在使用 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;
    }
}

代码中发生了什么

所以这基本上是一个 IntentService,其中包含一个 CountdownTimer 来向 Activity 广播消息。它还使用 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$1$override.onFinish(PomodoroService.java:44)
                                                                             at com.nursson.pomodorotimer.service.PomodoroService$1$override.access$dispatch(PomodoroService.java)
                                                                             at com.nursson.pomodorotimer.service.PomodoroService$1.onFinish(PomodoroService.java:0)
                                                                             at android.os.CountDownTimer$1.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,并且在执行此操作时成为一个“好公民”。

最终目标是拥有一个可以持续通过方向变化并在 Activity 终止时继续运行的计时器。

到目前为止我尝试过的:

  • 正在删除Looper.myLooper().quit();。然后,当 CountDownTimer 完成时,onDestroy() 永远不会被调用。
  • 删除所有Looper引用。然后 onDestroy() 被过早调用。计时器可以工作,但我无法获取对 CountDownTimer 的引用来取消它。

我的想法:

  • 有比 CountDownTimer 更适合我想做的事情的替代方案吗?也许是一个 AlarmManager?
  • 我是否应该使用 Service 而不是 IntentService,这样可以更轻松地处理 CountDownTimer?

最佳答案

我见过的所有Looper示例都使用这种模式

Looper.prepare();
// create a handler
Looper.loop();

它为没有消息循环的线程创建并运行消息循环,处理程序用于与循环交互。

现在在您的代码中,我既没有看到 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 并阻止服务在计时器完成之前完成。

或者使用另一种类型的服务——绑定(bind)或非绑定(bind),它在失业后不会自行破坏。但它们也有其缺点——两者都不是为了在主线程之外工作,这就是 IntentService 的用途。

关于java - 如何从 IntentService 中管理倒计时计时器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39200219/

相关文章:

java - System.in.read() 不读取

java - 无法运行两个 java webstart 实例

android - 如何通过 Android 中的 Intent 发送邮件?

android - 无法在设备 : error for Android 上执行 shell 命令 "getprop,dev.bootcomplete"“

android - LinearLayout 中的启动画面图像背景不适合

python - concurrent.futures.as_completed 是如何工作的?

java - 异构任务和同构任务

java - C 逼近函数

java - 从 2.3 升级后通过 Android Studio 2.3.3 安装 apk 的未知失败 (UnsatisfiedLinkError)

java - 为什么我的服务的 AsyncTask 会阻止来自主 Activity 的 AsyncTasks?