天天看點

Android Service startForeground() 可能不顯示Notification問題

Android中的Service分前台服務和背景服務兩類:

前台服務會在通知欄有一條不能被手動清除的Notification,當此前台服務由于記憶體不足而被系統kill掉的時候,此Notification也會同時消失,使用者由此得知此服務已經停止了,起到一個通知使用者服務是否還在工作;

背景服務則沒有類似的Notification,即使被系統kill掉,使用者也不會得到什麼通知。

Service通過調用方法 startForeground (int id, Notification notification)方法設定是否為前台任務,如:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
		        System.currentTimeMillis());
		Intent notificationIntent = new Intent(this, ViewServerActivity.class);
		PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
		notification.setLatestEventInfo(this, getText(R.string.notification_title,
		getText(R.string.notification_message), pendingIntent);
		startForeground(ONGOING_NOTIFICATION, notification);
           

由此代碼引發了一個需要特别注意的事情,代碼片段中的NOGOING_NOTIFICATION的值,不能是0,否則就不會顯示出Notification,究其原因隻能看源碼了:

service startForeground()和stopForeground()源碼:

public final void startForeground(int id, Notification notification) {
        try {
            mActivityManager.setServiceForeground(
                    new ComponentName(this, mClassName), mToken, id,
                    notification, true);
        } catch (RemoteException ex) {
        }
    }
           
public final void stopForeground(boolean removeNotification) {
        try {
            mActivityManager.setServiceForeground(
                    new ComponentName(this, mClassName), mToken, 0, null,
                    removeNotification);
        } catch (RemoteException ex) {
        }
    }
           

可見是有mActivityManager.setServiceForeground()裡面做了封裝,mActivityManager是一個IActivityManager接口的實作類,此接口實作類的關系具體參考此博文:

http://blog.csdn.net/stonecao/article/details/6579710

其實最終運作到的是ActiveServices類中的setServiceForegroundLocked方法:

public void setServiceForegroundLocked(ComponentName className, IBinder token,
            int id, Notification notification, boolean removeNotification) {
        final int userId = UserHandle.getCallingUserId();
        final long origId = Binder.clearCallingIdentity();
        try {
            ServiceRecord r = findServiceLocked(className, token, userId);
            if (r != null) {
                if (id != 0) {
                    if (notification == null) {
                        throw new IllegalArgumentException("null notification");
                    }
                    if (r.foregroundId != id) {
                        r.cancelNotification();
                        r.foregroundId = id;
                    }
                    notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
                    r.foregroundNoti = notification;
                    r.isForeground = true;
                    r.postNotification();
                    if (r.app != null) {
                        updateServiceForegroundLocked(r.app, true);
                    }
                    getServiceMap(r.userId).ensureNotStartingBackground(r);
                } else {
                    if (r.isForeground) {
                        r.isForeground = false;
                        if (r.app != null) {
                            mAm.updateLruProcessLocked(r.app, false, false);
                            updateServiceForegroundLocked(r.app, true);
                        }
                    }
                    if (removeNotification) {
                        r.cancelNotification();
                        r.foregroundId = 0;
                        r.foregroundNoti = null;
                    }
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
           

至此,我們應該很清楚了,如果傳進來的id=0,則直接執行以下代碼了:

if (removeNotification) {
         r.cancelNotification();
         r.foregroundId = 0;
         r.foregroundNoti = null;
 }
           

最後直接cancleNotification(),是以傳id=0,就相當于取消notification顯示!

從google上的文檔看到以下參數說明:

Parameters
id The identifier for this notification as per 

NotificationManager.notify(int, Notification)

; must not be 0.
notification The Notification to be displayed.
See Also
  • stopForeground(boolean)

但是在我的eclipse中的javadoc卻是看不到“must not be 0”的說明的!