Android组件管理框架—前台服务Service之startForegroundService方式启动流程(Android P)

本文详细解析了Android P中通过startForegroundService启动前台服务的流程,包括调用过程、区别于后台服务的特点,以及如何避免"Context.startForegroundService() did not then call Service.startForeground()"异常。主要内容涉及服务启动流程、通知机制和超时处理。
摘要由CSDN通过智能技术生成

一 前言

这节的内容也是由于项目中的一个Bug引起:

12-13 10:41:07.520 16661 16661 E AndroidRuntime: FATAL EXCEPTION: main
12-13 10:41:07.520 16661 16661 E AndroidRuntime: Process: cn.xxx.xxxxx:remote, PID: 16661
12-13 10:41:07.520 16661 16661 E AndroidRuntime: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{ed71832 u0 cn.xxx.xxxxx/com.amap.api.location.APSService}
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1879)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:106)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at android.os.Looper.loop(Looper.java:217)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:7356)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at java.lang.reflect.Method.invoke(Native Method)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:499)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:865)

       Android O 之前,创建前台服务的方式通常是先创建一个后台服务,然后将该服务推到前台。Android O及Android P,系统不允许后台应用创建后台服务,Android O 引入了一种全新的方法,即ContextCompat.startForegroundService() ,以在前台启动新服务。

      启动一个前台服务要做的事情有:

     (1) 调用 ContextCompat.startForegroundService() 可以创建一个前台服务,相当于创建一个后台服务并将它推到前台;

     (2)  创建一个用户可见的 Notification ;

     (3)  必须在5秒内调用该服务的 startForeground(int id, Notification notification) 方法,否则将停止服务并抛出 android.app.RemoteServiceException:Context.startForegroundService() did not then call Service.startForeground()异常。

什么是前台应用
满足以下任意条件的应用被视为处于前台:

具有可见的 Activity;
具有前台服务;
另一个前台应用关联到该应用,如输入法、壁纸服务、语音服务等。
如果以上条件均不满足,应用将被视为处于后台。

前台服务后台服务的区别
        前台服务会在通知一栏显示ONGOING的Notification,当服务被终止的时候,通知一栏的Notification也会消失,这样对于用户有一定的通知作用,常见的如音乐播放服务。

        默认的服务即为后台服务,即不会在通知一栏显示ONGOING的Notification。当服务被终止的时候,用户是看不到效果的,某些不需要运行或终止提示的服务,如天气更新、日期同步、邮件同步等。

       后台服务我们可以自己创建 ONGOING 的 Notification 这样就成为前台服务吗?答案是否定的,前台服务在做了上述工作之后需要调用 startForeground ( android 2.0 及其以后版本 )或 setForeground (android 2.0 以前的版本)使服务成为前台服务。这样的话,当服务被外部强制终止掉的时候,ONGOING 的 Notification 也会移除掉。

 

二 图示调用流程

      主要调用流程和StartService的流程基本相似。

 

三 具体代码流程

第一部分

         启动前台服务调用者调用startForegroundService,第一部分主要随着调用流程的分析我们看看为什么会出现Context.startForegroundService() did not then call Service.startForeground()异常。

1 frameworks/base/core/java/android/content/ContextWrapper.java

@Override
public ComponentName startForegroundService(Intent service) {
    return mBase.startForegroundService(service);
}

这里mBase变量是ContextImpl类型,是在创建activity的时候,new 一个ContextImpl对象,赋值给activity的。

2 frameworks/base/core/java/android/app/ContextImpl.java

@Override
public ComponentName startForegroundService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, true, mUser);
}

startServiceCommon

private ComponentName startServiceCommon(Intent service, boolean requireForeground,
        UserHandle user) {
    try {
        validateServiceIntent(service);
        service.prepareToLeaveProcess(this);
        ComponentName cn = ActivityManager.getService().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), requireForeground,
                        getOpPackageName(), user.getIdentifier());
        if (cn != null) {
            if (cn.getPackageName().equals("!")) {
                throw new SecurityException(
                        "Not allowed to start service " + service
                        + " without permission " + cn.getClassName());
            } else if (cn.getPackageName().equals("!!")) {
                throw new SecurityException(
                        "Unable to start service " + service
                        + ": " + cn.getClassName());
            } else if (cn.getPackageName().equals("?")) {
                throw new IllegalStateException(
                        "Not allowed to start service " + service + ": " + cn.getClassName());
            }
        }
        return cn;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

       和startService的流程相似,还是走到AMS的startService,requireForeground为true。可以先回看startService的流程Android组件管理框架—后台服务Service之startService方式启动流程(Android P)

3 frameworks/base/core/java/android/app/ActivityManager.java

4 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

调用到系统进程(System_server)来了。

@Override
public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, boolean requireForeground, String callingPackage, int userId)
        throws TransactionTooLargeException {
    enforceNotIsolatedCaller("startService");
    // Refuse possible leaked file descriptors
    if (service != null && service.hasFileDescriptors() == true) {
        throw new IllegalArgumentException("File descriptors passed in Intent");
    }
 
    if (callingPackage == null) {
        throw new IllegalArgumentException("callingPackage cannot be null");
    }
 
    if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
            "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
    synchronized(this) {
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        ComponentName res;
        try {
            res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid,
                    requireForeground, callingPackage, userId);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
        return res;
    }
}

接着看mServices.startServiceLocked,mServices是ActiveService的实例。

5 frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
        throws TransactionTooLargeException {
    if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
            + " type=" + resolvedType + " args=" + service.getExtras());
 
    final boolean callerFg;
    if (caller
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值