关于设置前台Service进程的误区

在Android开发中,设置Service为前台服务能防止系统回收,常用于音乐播放等场景。错误地将startForeground()的id设为0会导致问题。源码分析显示,id为0时系统无法正确管理前台Service,因此必须避免。停止前台Service时,无需指定id。
摘要由CSDN通过智能技术生成

关于设置前台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呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值