由 AlarmManager 重新创建的服务 [英] Service being re-Created by AlarmManager

查看:28
本文介绍了由 AlarmManager 重新创建的服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个相当标准的服务,我希望使用警报触发它.这是服务的启动部分:

I have a fairly standard Service which I wish to trigger using an alarm. Here's the initiation section of the service:

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 触发它时:

When I trigger it using context.startService from an activity, it starts absolutely normally. In particular, if it is already running it doesn't (re)start from scratch but simply enters the existing instantiation via onStartCommand(). This is the expected behaviour. However, when I trigger it using the 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() 在已经运行的实例化中.

When the alarm is due it seems to restart the service from scratch: it starts a new instantiation, calls onCreate() and then onStartCommand() rather than just calling onStartCommand() in the already running instantiation.

我已经尝试将 PendingIntent 标志更改为 FLAG_ONE_SHOT 并将 context 替换为 MyService.this 没有改进.

I have already tried changing the PendingIntent flag to FLAG_ONE_SHOT and replacing context with MyService.this with no improvement.

我对此感到很困惑 - 谁能解释这种行为并建议使其按预期运行的方法?

I am rather bemused by this - can anyone explain this behaviour and suggest ways to get it to behave as expected?

编辑 - 导致解决方案的操作集合在我下面的答案中.

EDIT - The collection of actions that resulted in a solution are in my answer below.

推荐答案

经过一些调查和工作,我发现了很多东西.完成所有这些后,这个问题似乎消失了:

After some investigation and work, I've discovered a number of things. Having done all of them, this problem looks like it's disappeared:

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

  1. If you override onStart and onStartCommand in a service (to allow for older devices) and you put super.onStartCommand in the latter, it will call onStart, meaning you get every intent coming in twice!

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

As per one of the other answers (and comments on it), the AlarmManager is designed and specified to deliver Broadcast intents, not other types. However, in practice, it isn't picky and seems to honour other forms. I think that this was one of the keys in resolving the issue.

如果该服务与其他活动等处于同一进程中,则该服务有时似乎刚刚重新启动".这可能是此问题中指出的问题的实际原因.参见 Android 服务 onCreate 被多次调用而不调用 onDestroy.

If the service is in the same process as other activites etc, the service sometimes seems to "just get restarted". This may be the actual cause of the issue noted in this question. See Android service onCreate is called multiple times without calling onDestroy.

当仅使用意图与服务通信而不是绑定和使用 Messenger 或绑定和访问方法时,事情似乎更稳定.虽然这两个都是正确的,但它们的管理非常复杂(尽管您可以使用这种方法:从服务线程调用 Android Activity 的首选方法是什么使用 Android 应用程序类来持久化数据).虽然我完全理解 android 文档不同意我的观点,但根据我的观察,转向广播意图似乎只有通信才是关键.如果您采用单独的流程方法,无论如何您都必须这样做.

Things seem to be more stable when solely using intents to communicate with the Service rather than binding and using a Messenger or binding and accessing methods. Whilst both of these are correct, they are quite complex to manage (although you could use this approach: What is the preferred way to call an Android Activity back from a Service thread and Using the Android Application class to persist data). Whilst I fully appreciate that the android docs disagree with me, in my observation moving to broadcast intent only communication seemed key. If you go for the separate process approach you'll have to do this anyway.

在声明和处理类的方式上保持一致是值得的.这有点乱,但是,因为有时使用全名(com.company.superapp.CleverService")而不是短(CleverService"或.CleverService")似乎要付出代价.所以,最好总是使用全名.

It pays to be consistent in how you declare and address your classes. It's a bit messy, but, because it sometimes seems to pay to use full names ("com.company.superapp.CleverService") rather than short ("CleverService" or ".CleverService"). So, it's probably better to always use full names.

关于上下文的经验法则(使用 getApplicationContext")并不是真正的正确方法.请参阅何时调用活动上下文或应用程序上下文?;本质上使用这个,除非你真的需要使用更广泛的上下文,并很好地管理你的变量.

The rule of thumb floating around out there about contexts ("use getApplicationContext") isn't really the right way to do it. See When to call activity context OR application context?; in essence use this, unless you really need to use a broader context, and manage your variables well.

如果在不再存在的活动、服务、线程、异步任务等中创建仍在使用的东西,垃圾收集器可能会清除它.如果应用程序是基于服务的,那么制作传入的类的副本可能是明智的,这样它们以后就不会被清除.

It's possible for the garbage collector to clear up something still in use if it was created in an Activity, Service, Thread, AsyncTask, etc that is no longer around. If the application is based around a service, it may be wise to make a copy of classes coming in so that they don't get cleared up later.

启动服务的一种比通常建议的更简洁的方法是为服务提供一个意图过滤器,并以其全名作为操作.然后,您可以创建意图以仅将类名作为字符串启动它.这意味着您不必担心上下文.请参阅调用服务中的问题.

A neater way to start a service than is often suggested is to give the service an intentFilter with it's full name as the action. You can then create the intent to start it with just the class name as a string. This means you don't have to worry about context. See Issue in Calling Service.

这篇关于由 AlarmManager 重新创建的服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆