由AlarmManager重新创建的服务

时间:2013-03-15 14:36:49

标签: android android-service android-alarms

我有一个相当标准的服务,我想用警报触发。这是该服务的启动部分:

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但没有任何改进。

我对此感到困惑 - 任何人都可以解释这种行为,并建议如何让它按预期行事吗?

编辑 - 导致解决方案的行动集合在我的答案中。

3 个答案:

答案 0 :(得分:4)

经过一番调查和工作,我发现了很多东西。完成所有这些后,这个问题看起来已经消失了:

  1. 如果您在服务中覆盖onStart和onStartCommand(以允许使用较旧的设备)并将super.onStartCommand放在后者中,它将调用onStart,这意味着您可以获得两次意图!

  2. 根据其他答案之一(及其评论),AlarmManager的设计和指定是为了提供广播意图,而不是其他类型。然而,在实践中,它并不挑剔,似乎尊重其他形式。我认为这是解决问题的关键之一。

  3. 如果服务与其他活动处于同一进程中,则该服务有时似乎“刚刚重启”。这可能是此问题中提到的问题的实际原因。请参阅Android service onCreate is called multiple times without calling onDestroy

  4. 当仅使用意图与服务进行通信而不是绑定和使用Messenger或绑定和访问方法时,事情似乎更稳定。虽然这些都是正确的,但它们管理起来相当复杂(尽管您可以使用这种方法:What is the preferred way to call an Android Activity back from a Service threadUsing the Android Application class to persist data)。虽然我完全理解android文档不同意我,但在我的观察中,转向广播意图只是沟通似乎很关键。如果你采用单独的过程方法,你无论如何都必须这样做。

  5. 在声明和解决课程方面保持一致是值得的。这有点乱,但是,因为有时似乎需要使用全名(“com.company.superapp.CleverService”)而不是short(“CleverService”或“.CleverService”)。因此,总是使用全名更好。

  6. 关于上下文浮动的经验法则(“使用getApplicationContext”)实际上并不是正确的方法。见When to call activity context OR application context?;本质上使用它,除非你真的需要使用更广泛的上下文,并妥善管理你的变量。

  7. 如果垃圾收集器在不再存在的Activity,Service,Thread,AsyncTask等中创建,它可能会清除仍在使用的内容。如果应用程序是基于服务的,那么制作一个类的副本可能是明智的,这样它们就不会在以后被清除。

  8. 启动服务的一种比通常建议的更简洁的方法是为服务提供一个intentFilter,其全名作为操作。然后,您可以使用类名作为字符串创建启动它的意图。这意味着您不必担心上下文。请参阅Issue in Calling Service

答案 1 :(得分:1)

嗯,我真的很惊讶它完全运行你的Service!您传递给PendingIntent的{​​{1}}需要是广播AlarmManager。所以你需要重新设计你的代码。 Intent会触发AlarmManagerBroadcastReceiver可以调用BroadcastReciever

请参阅AlarmManager.set()

的说明

答案 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

中使用非零请求代码

如果你退出任何一个步骤,它将无效。