在android中定期运行任务

时间:2011-06-16 06:30:31

标签: android multithreading

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

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

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

一开始就可以正常工作 作为测试,我将延迟设置为10秒,并将手机放置一小时左右,期望每10秒钟看一次日志条目(加上几毫秒的执行时间),并在开始时发生的事情,但是一旦我关闭屏幕出现了这种情况:

06-15 23:19:46.237: INFO/Service(12932): Starting
06-15 23:19:46.667: VERBOSE/DebugTask(12932): Fire
06-15 23:19:56.687: VERBOSE/DebugTask(12932): Fire
06-15 23:20:04.237: DEBUG/dalvikvm(12932): Debugger has detached; object registry had 1 entries
06-15 23:25:34.788: VERBOSE/DebugTask(12932): Fire
06-15 23:27:11.338: VERBOSE/DebugTask(12932): Fire <- Screen on
06-15 23:27:21.348: VERBOSE/DebugTask(12932): Fire
06-15 23:27:31.368: VERBOSE/DebugTask(12932): Fire
06-15 23:27:41.378: VERBOSE/DebugTask(12932): Fire
06-15 23:27:51.388: VERBOSE/DebugTask(12932): Fire
06-15 23:27:55.158: DEBUG/dalvikvm(12932): GC_EXTERNAL_ALLOC freed 127K, 47% free 2891K/5447K, external 2114K/2137K, paused 94ms
06-15 23:27:56.788: INFO/Service(12932): Shutting down
06-15 23:27:56.808: INFO/Service(12932): Service has stopped

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

在这种情况下,将它更改为计时器会有所不同吗? 由于线程问题,我宁愿不使用计时器,处理程序也很适合我。

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

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

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并使用警报管理器,而每个任务运行时都会获取部分唤醒锁,并在任务完成时释放它。

2 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:0)

试试这个:

    private void doSomethingRepeatedly() {
      timer.scheduleAtFixedRate( new TimerTask() {
            public void run() {

                  try{

                     new SendToServer().execute(); 

                  }
                  catch (Exception e) {
                      // TODO: handle exception
                  }

             }
            }, 0, 10000);
                     }
相关问题