我有一个应用程序,其任务定期运行。
我没有使用计时器,而是使用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");
}
}
我尝试拨打Service.startForeground(int, Notification)
,但没有任何区别。
经过大量挖掘后,我基本上需要获取partial wakelock才能在屏幕关闭时运行我的服务。
这当然会耗尽手机的电池,这是不太理想的 有没有办法以一定的间隔运行我的任务而没有一个恒定的唤醒锁?
我基本上希望我的应用程序在没有任何工作要做的时候睡觉,当一个任务运行,运行该任务然后再回去睡觉时醒来,这可能吗?
我知道有AlarmManager
我可能会使用但是这将很难实现,因为这将定期运行我的服务,而实际上我有多个线程,所有需要运行不同的间隔
我正在研究使用部分唤醒锁和警报管理器的相当复杂的解决方案,当我成功/失败时我会回发。
正如我在更新3中所写的那样,我的最终解决方案完全跳过sendDelayed并使用警报管理器,而每个任务运行时都会获取部分唤醒锁,并在任务完成时释放它。
答案 0 :(得分:2)
当设备进入休眠状态时,服务将暂停执行。没有办法解决这个问题。只有两个解决方案是您在更新中提到的解决方案,即WakeLock
和AlarmManager
。
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);
}