我有一个相当标准的服务,我想用警报触发。这是该服务的启动部分:
class MyService extends Service {
private Context context;
private AlarmManager alarmManager = null;
private final String startReason = "com.stuff.myreason";
private final int REASON_NO_INTENT = 0;
private final int REASON_ALARM = 1;
private final int REASON_X = 2; // and so on.
@Override
void onCreate() {
super.onCreate();
context = getApplicationContext();
alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
// do onCreate stuff
}
@Override
int onStartCommand (Intent intent, int flags, int startId) {
int reason = REASON_NO_INTENT;
if (intent != null) {
reason = intent.getExtra(startReason, REASON_NO_INTENT);
}
switch(reason) {
// handle the different reasons we may have been "started"
}
return START_STICKY;
}
}
当我使用来自活动的context.startService触发它时,它会正常启动。特别是,如果它已经在运行,它不会(重新)从头开始,而只是通过onStartCommand()
进入现有的实例化。这是预期的行为。但是,当我使用AlarmManager触发它时:
Intent intent = new Intent(context, MyService.class);
intent.putExtra(purposeOfStartCode, REASON_ALARM);
PendingIntent pi = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, /* A time in the future */, pi);
当警报到期时,它似乎从头开始重新启动服务:它启动一个新的实例化,调用onCreate()
然后调用onStartCommand()
而不是在已经运行的实例化中调用onStartCommand()
我已尝试将PendingIntent
标记更改为FLAG_ONE_SHOT
并将context
替换为MyService.this
但没有任何改进。
我对此感到困惑 - 任何人都可以解释这种行为,并建议如何让它按预期行事吗?
编辑 - 导致解决方案的行动集合在我的答案中。
答案 0 :(得分:4)
经过一番调查和工作,我发现了很多东西。完成所有这些后,这个问题看起来已经消失了:
如果您在服务中覆盖onStart和onStartCommand(以允许使用较旧的设备)并将super.onStartCommand放在后者中,它将调用onStart,这意味着您可以获得两次意图!
根据其他答案之一(及其评论),AlarmManager的设计和指定是为了提供广播意图,而不是其他类型。然而,在实践中,它并不挑剔,似乎尊重其他形式。我认为这是解决问题的关键之一。
如果服务与其他活动处于同一进程中,则该服务有时似乎“刚刚重启”。这可能是此问题中提到的问题的实际原因。请参阅Android service onCreate is called multiple times without calling onDestroy。
当仅使用意图与服务进行通信而不是绑定和使用Messenger或绑定和访问方法时,事情似乎更稳定。虽然这些都是正确的,但它们管理起来相当复杂(尽管您可以使用这种方法:What is the preferred way to call an Android Activity back from a Service thread和Using the Android Application class to persist data)。虽然我完全理解android文档不同意我,但在我的观察中,转向广播意图只是沟通似乎很关键。如果你采用单独的过程方法,你无论如何都必须这样做。
在声明和解决课程方面保持一致是值得的。这有点乱,但是,因为有时似乎需要使用全名(“com.company.superapp.CleverService”)而不是short(“CleverService”或“.CleverService”)。因此,总是使用全名更好。
关于上下文浮动的经验法则(“使用getApplicationContext”)实际上并不是正确的方法。见When to call activity context OR application context?;本质上使用它,除非你真的需要使用更广泛的上下文,并妥善管理你的变量。
如果垃圾收集器在不再存在的Activity,Service,Thread,AsyncTask等中创建,它可能会清除仍在使用的内容。如果应用程序是基于服务的,那么制作一个类的副本可能是明智的,这样它们就不会在以后被清除。
启动服务的一种比通常建议的更简洁的方法是为服务提供一个intentFilter,其全名作为操作。然后,您可以使用类名作为字符串创建启动它的意图。这意味着您不必担心上下文。请参阅Issue in Calling Service。
答案 1 :(得分:1)
嗯,我真的很惊讶它完全运行你的Service
!您传递给PendingIntent
的{{1}}需要是广播AlarmManager
。所以你需要重新设计你的代码。 Intent
会触发AlarmManager
,BroadcastReceiver
可以调用BroadcastReciever
。
答案 2 :(得分:0)
我使用以下代码使其工作:
AlarmManager almgr = (AlarmManager)MyContext.getSystemService(Context.ALARM_SERVICE);
Intent timerIntent = new Intent(MyUniqueLabel);
timerIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
PendingIntent pendingOffLoadIntent = PendingIntent.getBroadcast(MyContext, 1, timerIntent, 0);
你必须做这些事情才能发挥作用
1.)调用addFlags和intent并将其传递给FLAG_RECEIVER_FORGROUND
2.)在PendingIntent.getBroadcast
如果你退出任何一个步骤,它将无效。