START_STICKY,前台Android服务无提示消失

8

我在我的新应用程序中启动了一个服务。该服务是前台服务,并带有通知。当在AVD 2.1 API Level 7上运行时,一切正常。但当在运行Gingerbread的三星Galaxy Tab上运行时,服务会启动(图标和应用程序名称出现在通知区域的顶部),但几秒钟后,服务就消失了。我能看到日志中与我的应用相关的最后一个条目是我的Log.d("Taglines","Returning with " + START_STICKY),它紧接着我的Service的onStartCommand覆盖中的"return START_STICKY ;",如下所示:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    int rc ;
    Log.d("Taglines","onStartCommand()");
    Toast.makeText(this, "Starting service TagsManager", Toast.LENGTH_SHORT).show();
    Log.d("Taglines","Calling super.onStartCommand()");
    rc = super.onStartCommand(intent,flags,startId);
    Log.d("Taglines","super.onStartCommand return code was " + rc);
    createNotification(INITIAL_NOTIFICATION_TEXT);
    Log.d("Taglines","Returning with " + START_STICKY);
    return START_STICKY ;
}

通知的设置方式如下所示:

void createNotification(String text) {

    Log.d("Taglines","createNotification called");
    if (mNotificationManager == null) {
        // Get a reference to the Notification Manager
        String ns = Context.NOTIFICATION_SERVICE;
        mNotificationManager = (NotificationManager) getSystemService(ns);
        Log.d("Taglines","Obtained reference to Notification Manager");
    }

    // Instantiate the Notification
    int icon = R.drawable.ic_notification;
    CharSequence tickerText = "Taglines";
    long when = System.currentTimeMillis();

    notification = new Notification(icon, tickerText, when);

    // Define Notification's expanded message and intent
    Log.d("Taglines","createNotificacion() .. getApplicationContext");
    context = getApplicationContext();
    contentText = text;
    // notificationIntent = new Intent(this, TagsOverview.class);
    notificationIntent = new Intent(this, TagsServiceMenu.class);
    contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

    notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);

    // Pass the Notification to the NotificationManager: 
    Log.d("Taglines","createNotificacion() ... passing notification");
    mNotificationManager.notify(NOTIFICATION_ID, notification);
    Log.d("Taglines","Starting foreground");
    startForeground(NOTIFICATION_ID, notification);
    Log.d("Taglines","Started");
}

当服务启动时,以下是“adb logcat”的结果:
D/Taglines(21863): Starting service
D/Taglines(21863): TagsManager(nullary) completed
D/Taglines(21863): onStartCommand()
D/Taglines(21863): Calling super.onStartCommand()
D/Taglines(21863): super.onStartCommand eturn code was 2
D/Taglines(21863): createNotification called
D/Taglines(21863): Obtained reference to Notification Manager
D/Taglines(21863): createNotificacion() .. getApplicationContext
D/Taglines(21863): createNotificacion() ... passing notification
D/Taglines(21863): Starting foreground
D/Taglines(21863): Started
D/Taglines(21863): Returning with 1

之后,没有什么特别的(来自PID 21863的任何东西都没有)。只是一堆:

D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
W/InputManagerService(  302): Window already focused, ignoring focus gain of:         com.android.internal.view.IInputMethodClient$Stub$Proxy@40bc06e8
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false
D/KeyguardViewMediator(  302): setHidden false

我认为在这种情况下不需要,但以下是 AndroidManifest.xml 的相关部分:

    <service android:name=".TagsManager"
             android:exported="false">
    </service>

我哪里做错了?我还能提供什么其他信息吗?

嗯...我阅读了更多关于startForeground的资料,似乎它只是在通知对象上设置一个标志,而不是真正采取某些操作。因此,凭直觉,我颠倒了序列,现在startForeground在NotificationManager.notify之前。到目前为止,看起来效果不错。现在我正在寻找那个让我按照另一种方式去做的例子... - Dennis
遗憾的是,反转该序列的成功是短暂的。似乎有点赌博,黏性是否会“粘住”。尝试启动此服务已经尝试了许多次,到目前为止只有三次成功启动。:( - Dennis
你的Galaxy Tab是否遇到了低内存情况,或者你的服务在主线程上执行了一些占用内存较多的操作? - IgorGanapolsky
2个回答

11

几件事情:

  1. 去掉 mNotificationManager.notify(NOTIFICATION_ID, notification);startForeground() 会为您显示通知图标。

  2. 前台 Service 仍然可能被杀死,只是不太可能。

  3. 在2.3中存在一个错误(不确定是否已经修复),当一个 Service 被杀死并重新启动时,它的 onStartCommand() 不会再次被调用。相反,您需要在 onCreate() 中进行任何设置。


非常感谢,这一切都很有道理。我已经尝试了你说的,目前看起来情况不错。 - Dennis
+1 非常感谢。2.3版本中有一个bug,iOS也有一个bug...如果操作系统到处都是bug,如何为我的客户编写软件呢?- 是的,使用平台无关的框架!:) 我已经使用了START_STICKY和onDestroy()有时会重新创建... - user529543

2

当手机内存不足且在执行过程中被杀死服务时,这两个代码才会变得重要。START_STICKY 告诉操作系统在有足够内存时重新创建服务,并再次使用空意图调用 onStartCommand()START_NOT_STICKY 告诉操作系统不要再次重新创建服务。还有第三个代码 START_REDELIVER_INTENT ,它告诉操作系统重新创建服务并将相同的意图传递到 onStartCommand()

这篇文章 by Dianne Hackborn 更好地解释了这一背景,比官方文档更易懂。

这里的关键部分是函数返回的新结果代码,它告诉系统如果服务的进程在运行时被杀死时该怎么做:

START_STICKY基本上与以前的行为相同,服务将保持“启动”状态,并稍后由系统重新启动。与平台以前的版本唯一的区别是,如果由于其进程被杀死而被重新启动,则在服务的下一个实例上调用onStartCommand()时将使用空Intent而不是根本不调用。使用此模式的服务应始终检查此情况并适当处理。
START_NOT_STICKY表示,在从onStartCreated()返回后,如果进程被杀死且没有剩余的启动命令要传递,则该服务将停止而不是重新启动。这对于仅在执行发送到它们的命令时运行的服务更有意义。例如,可以从闹钟每15分钟启动服务以轮询某些网络状态。如果在执行该工作时被杀死,则最好让它停止,并在下次闹钟触发时重新启动。
START_REDELIVER_INTENT类似于START_NOT_STICKY,但是,如果服务的进程在为给定意图调用stopSelf()之前被杀死,则该意图将被重新传递给它,直到完成(除非在更多尝试后仍无法完成,在这种情况下,系统会放弃)。这对于接收要做的工作命令的服务很有用,并希望确保它们最终完成每个发送的命令的工作。

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