【问题标题】:Using startForeground() with an Intent Service将 startForeground() 与 Intent 服务一起使用
【发布时间】:2016-12-03 00:28:16
【问题描述】:

我正在尝试保持对屏幕开/关变化做出反应的服务。该服务会在一段时间内完美运行,但最终它会被杀死。我现在正在尝试使用 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;
        }

}

【问题讨论】:

  • 我有类似的问题,在我的情况下,我不得不保持闪光灯亮着,但它在几秒钟内停止了,我发现唯一可行的选择是在意图服务中使用另一个服务并运行它服务作为前台服务,这解决了问题。如果您希望我发布更多详细信息,请告诉我
  • 嗨@user2548816,我知道已经有一段时间了,但我很想听听您的解决方案。你能详细说明一下吗?

标签: android


【解决方案1】:

(更新)我想可能有以下几种情况:

1) documentation 表示 IntentService 状态:

服务根据需要启动,依次使用 工作线程,并在工作结束时自行停止。

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

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

3) 在极少数情况下,系统仍然可以杀死前台进程 - 因此,如果您在 onHandleIntent() 中进行大量分配(真的很多)和其他一些工作(而不是“ Blah Blah Blah”在你的代码中)——你可能会遇到它——但我想不是这样的。

由于问题的标题是“使用带有 IntentService 的 startForeground()” - 也想澄清一下: 我相信没有什么(架构、最佳实践、android 框架、IntentService 的 java 文档)可以阻止您将 Intent 服务作为前台运行。当然,您需要仔细考虑它的用法以及您是否真的需要前台服务。一些想法是可用的here。有关示例代码,请参见下文。 (如果您将多个作业/意图排队到 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();
        }
    }
}

【讨论】:

  • 计算机编程的一般规则是:永远不要对其他人代表您获得的资源做出假设。你没有创建IntentService使用的后台线程;因此,您不应该对可以安全使用它的时间做出任何假设。我们在AsyncTask 中遇到了这个问题,Google 更改了execute() 的规则,将您限制为所有任务的单个线程。如果您想保持一个线程的时间超过一个短暂的时间,请创建您自己的线程。对推荐糟糕的编程实践投反对票。
  • @CommonsWare 我明白你的意思并理解它。那讲得通。实际上,我非常不同意您所说的“通常情况下,它的寿命不应超过几秒钟”。我不想做出假设,而只是阅读 java 文档,而 java 文档没有任何假设,例如“它通常不应该存活超过几秒钟。”
【解决方案2】:

IntentServiceonHandleIntent() 完成时自动关闭。它是在发生某些事情时执行一些简短的工作。通常,它的寿命不应超过几秒钟。

我假设这与我写的 in your last question in this area 相关。


您的应用程序的其余部分将注册和取消注册屏幕开/关事件的BroadcastReceiver - 显然,从您的 cmets 来看,这是一个活动。如果这些事情发生时您想要做的事情非常非常快(大约几毫秒),只需在onReceive() 中完成工作,然后就可以完成。

另一方面,如果您的工作量超过几毫秒,则需要让其他可以在后台线程上完成工作的东西来完成这项工作。例如,如果注册BroadcastReceiver 的“应用程序的其余部分”确实是一个活动,则该活动可能只是产生一个AsyncTask 来完成这项工作。

另一种可能性是使用IntentService。在最后一个问题之前,您选择在工作中走这条路。我不知道为什么。不管怎样,IntentService,就像AsyncTask,应该是一个短暂的组件——你通过startService()向它发送一个命令,它在onHandleIntent()中工作,然后它就消失了。

考虑到所有这些,让我们谈谈你的具体观点。


该服务会在一段时间内完美运行,但最终它会被杀死。

不清楚你认为“被杀”是什么意思。一旦onHandleIntent() 返回,IntentService 就会自动消失,理想情况下应该在几秒钟内发生。

我现在正在尝试使用 startForeground() 来保持进程处于活动状态,但它似乎仍在死亡。

再一次,不清楚你认为“死亡”是什么意思。请记住,IntentService 的存在并不能阻止 CPU 在屏幕关闭后关闭,startForeground() 与此无关。

另外,作为旁注,Logcat 抱怨泄漏,因为 unregisterReceiver() 未被调用(除非用户手动按下按钮)..

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

【讨论】:

  • 文档中没有任何内容表明“它的寿命不应超过几秒钟”。你在编造。如果他们愿意,服务可以永远存在。 IntentService 与实现普通的 Service 类没有什么不同。
  • @AndroidDev:“如果他们愿意,服务可以永远存在”——除了他们不能,因为用户和/或操作系统会在需要时摆脱它们。 “IntentService 与实现普通的 Service 类没有什么不同”——在您看来,Integer 与普通的 Object 类没有什么不同。
  • 来自文档:If the service is declared to run in the foreground (discussed later), then it will almost never be killed - developer.android.com/guide/components/services.html。相同的文档还说:(IntentService) is the best option if you don't require that your service handle multiple requests simultaneously,但如果你想要一个长期/无限期运行的服务,它没有提到 IntentService 是一个糟糕的选择。
  • 此外,如果您查看 IntentService 的源代码 - grepcode.com/file/repository.grepcode.com/java/ext/… - 它会显示 (Requests) may take as long as necessary。即,没有提及几秒钟或几秒钟等。因此,前台 IntentService 似乎确实是实现真正持续服务的最佳和最简单的方法。
  • 被否决,因为:IntentService 文档摘录:“所有请求都在单个工作线程上处理——它们可能需要尽可能长的时间(并且不会阻塞应用程序的主线程)循环),但一次只会处理一个请求。”几秒钟左右什么也没说。所以,答案是关于 IntentServices developer.android.com/reference/android/app/IntentService.html 的误导
【解决方案3】:

如果内存不足并且你消耗了太多内存并且你在后台坐得太久,那么你将被 ActivityManager 杀死。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多