【发布时间】:2011-06-16 06:30:30
【问题描述】:
我有一个应用程序,它有定期运行的任务。
我没有使用计时器,而是使用Handler.sendMessageDelayed(Message, long)。
我的服务启动一个Thread,完成后,它会向服务的处理程序发送一条消息,该处理程序又会向自身发送一条延迟消息,说明它应该再次启动任务。
这很好,一开始。
作为测试,我将延迟设置为 10 秒,然后将手机放置一个小时左右,期望每 10 秒(加上几毫秒的执行时间)看到一个日志条目,一开始就是这样,但只要我关闭发生这种情况的屏幕:
这似乎只在屏幕关闭时发生。这可能是一个优先问题吗?其他东西正在运行时服务会暂停吗?
在这种情况下,将其更改为计时器会有所不同吗?
由于线程问题,我宁愿不使用计时器,处理程序很好地为我解决了这个问题。
下面我附上了应用程序的相关代码,我很乐意提供您可能需要的任何其他内容。
我的服务的处理程序处理启动任务以外的其他消息,但没有其他类型的消息长时间运行,它们涉及显示通知并将消息委托给其他处理程序,因此我不相信服务线程正忙于处理其他消息。
该服务可能正忙于其他事情,但是我不确定服务在其生命周期内可能会获得什么调用。
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 并改用警报管理器,而每个任务正在运行时,会获取部分唤醒锁,并在任务完成时释放它。
【问题讨论】: