关于设置前台Service进程的误区
在开发中,为了保证自己的Service不会因为系统资源紧张而被回收,会为Service设置为前台服务。默认的Service服务为后台服务。常见的应用场景就是音乐播放器。
设置Service为前台服务很简单,在API大于5的系统上,只需在onStartCommand()方法中添加如下代码即可:
startForeground(ID, new Notification());
服务销毁时调用:
stopForeground(boolean removeNotification)
其中参数ID是通知的标识,NotificationManager可以通过此标识来更新Notification。如果没有仔细的阅读startForeground(int id, Notification notification)方法的注释,很容易就会为ID赋值为0。而注释中的参数解释很明确,这个id值一定不能是0.读者可以去源码中自己查看。
那么为什么不能设置为0呢,我们就需要查看系统源码了。
查询源码路径为
android.app.Service --> android.app.IActivityManager --> android.app.ActivityManagerNative --> android.app.ActivityManagerProxy --> com.android.server.am.AcivityManagerService --> com.android.server.am.ActiveServices
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) {
//此处判断了id是否为0
if (id != 0) {
//如果不为0,才会将Service锁定为前台服务
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 {
//如果是0的话,会取消前台服务锁定
if (r.isForeground) {
r.isForeground = false;
if (r.app != null) {
mAm.updateLruProcessLocked(r.app, false, null);
updateServiceForegroundLocked(r.app, true);
}
}
if (removeNotification) {
r.cancelNotification();
r.foregroundId = 0;
r.foregroundNoti = null;
} else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
r.stripForegroundServiceFlagFromNotification();
}
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
那么这个id在什么时候为0.答案就在Service中的停止前台服务的方法中:
public final void stopForeground(boolean removeNotification) {
try {
mActivityManager.setServiceForeground(
new ComponentName(this, mClassName), mToken, **0**, null,
removeNotification);
} catch (RemoteException ex) {
}
}
结论:在设置前台服务时,Notification的id不能为0.
那为什么在startForeground时需要指定id,而stopForeground时不需要指定id呢?