使用startForeground()与Intent Service

5

我正在尝试保持一项响应屏幕开/关变化的服务运行。该服务会在一段时间内完美地工作,但最终会被杀死。我现在尝试使用startForeground()来保持进程的活跃状态,但它似乎仍然会死亡。我知道没有办法永远保持进程的不间断运行,但我觉得我肯定做错了什么,因为添加startForeground()并没有为进程带来额外的生命周期。此外,顺便提一下,Logcat会抱怨有一个泄漏,因为unregisterReceiver()没有被调用(除非由用户手动按下按钮)...但是,由于我想要完成的任务性质,接收器需要一直运行,直到明确停止。

有什么建议吗?

相关代码:

public class UpdateService extends IntentService {

        public UpdateService() {
        super(null);

    }

        @Override
        protected void onHandleIntent(Intent intent) {

            final int myID = 1234;


            Intent notificationintent = new Intent(this, Main.class);
            notificationintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            PendingIntent pendIntent = PendingIntent.getActivity(this, 0, notificationintent, 0);


            Notification notice = new Notification(R.drawable.icon_image, "***********", System.currentTimeMillis());


            notice.setLatestEventInfo(this, "*************", "***********", pendIntent);

            notice.flags |= Notification.FLAG_NO_CLEAR;
            startForeground(myID, notice);

            boolean screenOn = intent.getBooleanExtra("screen_state", false);


// Blah Blah Blah......


        }

        @Override
        public IBinder onBind(Intent arg0) {
            // TODO Auto-generated method stub
            return null;
        }

}

1
我曾经遇到过类似的问题,在我的情况下,我必须保持手电筒开启,但是它在几秒钟后就停止了。我发现唯一可行的选择是在Intent Service内部使用另一个Service,并将该Service作为前台服务运行,这解决了问题。如果您想让我进一步详细说明,请告诉我。 - user2548816
嗨@user2548816,我知道已经有一段时间了,但我会很感兴趣听听您的解决方案。您可以详细说明吗? - kilokahn
3个回答

7

(更新) 我认为有以下可能情况:

1) IntentService 文档中说明:

服务根据需要启动,使用工作线程逐个处理每个 Intent,并在没有任务时停止自己。

因此,您的服务可能在 onHandleIntent() 完成后通常会停止(特别是,您提到 startForeground() 没有为进程增加额外的生命周期)。

2) 您可以尝试检查它是否与设备进入睡眠状态有关(或者您是通过计划启动服务并唤醒设备 - 在这种情况下,您可能需要获取WakeLock

3) 在非常罕见的情况下,系统仍然可能会杀死前台进程 - 因此,如果您在 onHandleIntent() 中执行了大量分配(真的很多)和其他工作(而不是在您的代码中的 "Blah Blah Blah"),则可能会遇到该问题 - 但我认为这不是该情况。

由于问题标题是“在 IntentService 中使用 startForeground()” - 我想澄清一下: 我相信没有什么(架构,最佳实践,Android 框架,IntentService 的 Java 文档)阻止您将 IntentService 运行为前台。当然,您需要仔细考虑其使用方式以及您是否实际上需要前台服务。一些想法可以在这里找到。有关示例代码,请参见下文。(如果您将多个作业/意图排队到 IntentService 中,则示例代码可能会显示多个通知,因此根据您的需求可能会有更好的解决方案。)


public class ForegroundService extends IntentService {

    private static final String TAG = "FrgrndSrv";

    public ForegroundService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        Notification.Builder builder = new Notification.Builder(getBaseContext())
                .setSmallIcon(R.drawable.ic_foreground_service)
                .setTicker("Your Ticker") // use something from something from R.string
                .setContentTitle("Your content title") // use something from something from
                .setContentText("Your content text") // use something from something from
                .setProgress(0, 0, true); // display indeterminate progress

        startForeground(1, builder.build());
        try {
            doIntesiveWork();
        } finally {
            stopForeground(true);
        }
    }

    protected void doIntesiveWork() {
        // Below should be your logic that takes lots of time
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

1
计算机编程的一个通用规则是:永远不要对其他资源代表你获取的资源做出假设。您没有创建IntentService使用的后台线程,因此不应该对您可以安全使用它的时间做出任何假设。我们在AsyncTask中遇到了这个问题,Google更改了execute()的规则,限制您在所有任务中只能使用单个线程。如果您想长时间持有线程,请创建自己的线程。推荐糟糕的编程实践会被踩低评分。 - CommonsWare
3
@CommonsWare,我理解你的观点并认同它。那很有道理。实际上,我对你所说的“它通常不应该存活超过几秒钟”这个说法持相当不同意见。我没有试图做出任何假设,只是阅读了Java文档,而Java文档中并没有类似“它通常不应该存活超过几秒钟”这样的假设。 - GregoryK

6
IntentServiceonHandleIntent()完成后会自动关闭。它用于在发生事件时执行简短的工作。通常情况下,它不应该存活超过几秒钟。

我假设这与我在之前的一个问题中所写的有关


你的应用程序的其他部分将注册和取消注册屏幕开/关事件的BroadcastReceiver —— 根据您的评论,这是一个活动。如果您想要在发生这些事件时执行的工作非常快(约为几毫秒),只需在onReceive()中执行并完成即可。

另一方面,如果您的工作量多于几毫秒,请使用其他可以在后台线程上执行工作的组件进行处理。例如,如果“你的应用程序的其他部分”注册了BroadcastReceiver确实是一个活动,则该活动可以仅生成一个AsyncTask来执行工作。

另一种可能性是使用IntentService。您之前已经选择了这条路线。我不知道为什么。无论如何,IntentServiceAsyncTask一样,应该是短暂的组件 —— 您通过startService()发送命令,它在onHandleIntent()中执行其工作,然后消失。

考虑到所有这些,请让我们谈谈您的具体问题。


服务一段时间后将完美地运行,但最终会被杀死。

不清楚您认为“被杀死”的含义是什么。 IntentService自动关闭一旦onHandleIntent()返回,并且理想情况下应该在几秒钟内完成。

我现在尝试使用startForeground()来保持进程存活,但似乎仍然无法正常运行。

再次强调,不清楚您认为“无法正常运行”是什么意思。请记住,仅有IntentService实例并不能阻止CPU在屏幕关闭时关闭,而startForeground()与此无关。

另外,顺带一提,Logcat抱怨有泄漏,因为未调用unregisterReceiver()(除非由用户手动按下按钮)...

您还需要在用户退出活动之前取消注册接收器。通常最好在onResume()中调用registerReceiver()并在onPause()中调用unregisterReceiver()


15
文档中没有任何内容表明“服务不应该运行超过几秒钟”。这是你自己臆造的。如果愿意,服务可以一直运行下去。IntentService与实现普通Service类没有区别。 - Johann
6
“如果它们愿意,服务可以永远存在”,但是当用户和/或操作系统希望摆脱它们时,它们就不能存在了。 “IntentService与实现普通Service类没有区别” - 据您的看法,一个整数与普通对象类没有区别。 - CommonsWare
8
从文档中得知:如果声明服务在前台运行(稍后会讨论),那么它几乎不会被杀死 - http://developer.android.com/guide/components/services.html 。同样的文档还说:(IntentService)如果你不需要你的服务同时处理多个请求,则是最佳选择,但它并没有提到如果你想要长时间或无限期运行的服务,IntentService是否是一个不好的选择。 - ban-geoengineering
7
此外,如果你查看IntentService的源代码 - http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/app/IntentService.java - 它说“(请求)可以花费任何必要的时间”。也就是说,没有提到几秒钟或一小段时间等。因此,前台IntentService似乎是实现真正持续服务的最佳和最简单的方式。 - ban-geoengineering
我在使用Intent Service时理解到的是,一旦onHandleIntent函数中的所有代码都执行完毕,它就会调用stop self。为了解决这个问题,我不得不在onHandleIntent内部启动另一个服务,并将该服务设置为前台服务。(可能不是最好的方法,但我仍然实现了我想要的功能) - user2548816
2
因为以下原因被踩:从IntentService文档中摘录:“所有请求都在单个工作线程上处理 - 它们可能需要尽可能长的时间(并且不会阻塞应用程序的主循环),但一次只能处理一个请求。” 没有提到几秒钟左右的时间。 因此,答案对IntentServices的描述是误导性的。https://developer.android.com/reference/android/app/IntentService.html - GregoryK

0
如果内存不足,你的内存消耗过多,并且你长时间处于后台状态,那么你将被ActivityManager杀死。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接