【问题标题】:Running tasks at regular intervals in android在android中定期运行任务
【发布时间】:2011-06-16 06:30:30
【问题描述】:

我有一个应用程序,它有定期运行的任务。

我没有使用计时器,而是使用Handler.sendMessageDelayed(Message, long)

我的服务启动一个Thread,完成后,它会向服务的处理程序发送一条消息,该处理程序又会向自身发送一条延迟消息,说明它应该再次启动任务。

这很好,一开始。
作为测试,我将延迟设置为 10 秒,然后将手机放置一个小时左右,期望每 10 秒(加上几毫秒的执行时间)看到一个日志条目,一开始就是这样,但只要我关闭发生这种情况的屏幕:

06-15 23:19:46.237:信息/服务(12932):开始 06-15 23:19:46.667:详细/调试任务(12932):火 06-15 23:19:56.687:详细/调试任务(12932):火 06-15 23:20:04.237:DEBUG/dalvikvm(12932):调试器已分离;对象注册表有 1 个条目 06-15 23:25:34.788:详细/调试任务(12932):火 06-15 23:27:11.338:详细/调试任务(12932):火

这似乎只在屏幕关闭时发生。这可能是一个优先问题吗?其他东西正在运行时服务会暂停吗?

在这种情况下,将其更改为计时器会有所不同吗?
由于线程问题,我宁愿不使用计时器,处理程序很好地为我解决了这个问题。

下面我附上了应用程序的相关代码,我很乐意提供您可能需要的任何其他内容。

我的服务的处理程序处理启动任务以外的其他消息,但没有其他类型的消息长时间运行,它们涉及显示通知并将消息委托给其他处理程序,因此我不相信服务线程正忙于处理其他消息。
该服务可能正忙于其他事情,但是我不确定服务在其生命周期内可能会获得什么调用。

Service.java:

private void startTask(Class<? extends Task> taskClass) {
    Task task = null;
    try {
        Constructor<? extends Task> c = taskClass.getConstructor();
        task = c.newInstance();
    } catch (Exception e) {
        Log.e("Service", "Failed to create class of type " + taskClass.getName(), e);
    }

    if(task != null) {          
        runningTasks.put(taskClass, task);
        task.start();
    }
}

private void taskDone(Class<? extends Task> taskClass) {
    runningTasks.remove(taskClass);

    SharedPreferences prefs = getPreferences();

    try {
        long interval = (Long)taskClass.getDeclaredMethod("getInterval", SharedPreferences.class).invoke(null, prefs);

        if(interval < 0)
            return;
        else {
            Message msgTmp = handler.obtainMessage(ServiceHandler.START_TASK, taskClass);
            handler.sendMessageDelayed(msgTmp, interval*1000);
        }

    }
    catch(NoSuchMethodException e) {
        Log.w("Service", String.format("Task %s does not implement method getInterval", taskClass.getSimpleName()));
    } catch (Exception e) {
        Log.w("Service", String.format("Could not call getInterval on task %s", taskClass.getSimpleName()), e);
    }
}

public class ServiceHandler extends Handler {
    /* Constants */
    public static final int TASK_DONE = 1; // obj contains the class
    public static final int START_TASK = 2; // obj contains the class

    @SuppressWarnings("unchecked")
    public void handleMessage(Message msg) {
        if(msg.what == TASK_DONE)
            taskDone((Class<? extends Task>)msg.obj);

        else if(msg.what == START_TASK)
            startTask((Class<? extends Task>) msg.obj);
    };
};

Task.java:

public abstract class Task extends Thread {
    @Override
    public final void run() {
        preRunTask();
        runTask();
        postRunTask();
        sendFinish();
    }

    private synchronized void sendFinish() {
        if(serviceHandler == null)
            return;
        Message m = serviceHandler.obtainMessage(ServiceHandler.TASK_DONE, getClass());
        serviceHandler.sendMessage(m);
    }
}

DebugTask.java:

public DebugTask extends Task {
    public static long getInterval(SharedPreferences prefs) {
        return Long.parseLong(prefs.getString(FREQUENCY_KEY, "-1"));
    }

    @Override
    protected void runTask() {
        Log.v("DebugTask", "Fire");
    }
}

更新 1

我试过打电话给Service.startForeground(int, Notification),但没有任何区别。


更新 2

经过大量挖掘后发现,我基本上需要获取partial wakelock 才能在屏幕关闭时运行我的服务。

这当然会耗尽手机的电池,而这并不是真正需要的。
有没有什么方法可以让我的任务在一定的时间间隔内运行而无需持续的唤醒锁?

我基本上希望我的应用在没有任何工作要完成时进入睡眠状态,并在运行任务时唤醒,运行该任务然后重新进入睡眠状态,这可能吗?

我知道有AlarmManager 我可能会使用它,但这将很难实现,因为它会定期运行我的服务,而实际上我有多个线程都需要运行不同的时间间隔。


更新 3

我正在使用部分唤醒锁和警报管理器开发一个相当复杂的解决方案,我会在成功/失败时回复。


最终更新

正如我在更新 3 中所写,我的最终解决方案完全跳过 sendDelayed 并改用警报管理器,而每个任务正在运行时,会获取部分唤醒锁,并在任务完成时释放它。

【问题讨论】:

    标签: android multithreading


    【解决方案1】:

    当设备进入睡眠状态时,服务将暂停执行。没有办法解决这个问题。仅有的两个解决方案是您在更新中提到的解决方案,即WakeLockAlarmManager

    AlarmManager 将是避免电池耗尽的首选解决方案。如果您需要在不同的时间间隔执行任务,您可以使用setInexactRepeating()。您可以为每个需要执行的任务设置一个闹钟。

    您仍然需要在完成工作所需的时间内添加唤醒锁,因为闹钟会唤醒设备,但不会长时间保持唤醒状态。有关它的实现,请在此处查看 CommonsWare 的示例代码:https://github.com/commonsguy/cwac-wakeful

    【讨论】:

    • 这并不完全正确,如果服务在前台,它不会停止,但是不会给它任何 CPU 时间。
    • 否则你的解决方案就是我实现的那个,AlarmManager,它触发获取唤醒锁并在完成后释放唤醒锁的任务。
    • @Nicklas 是的,你说得对,我的意思是通俗意义上的停止,即它将“停止运行”,直到手机唤醒。我将编辑它以“暂停执行”以使短语更清晰。
    【解决方案2】:

    试试这个:

        private void doSomethingRepeatedly() {
          timer.scheduleAtFixedRate( new TimerTask() {
                public void run() {
    
                      try{
    
                         new SendToServer().execute(); 
    
                      }
                      catch (Exception e) {
                          // TODO: handle exception
                      }
    
                 }
                }, 0, 10000);
                         }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-19
      • 1970-01-01
      • 2011-04-19
      • 1970-01-01
      • 2015-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多