Service启动流程总结-start和stop service

2023-10-31

回顾

Activity启动系列总结:
Activity启动流程总结-ActivityThread
Activity启动流程总结-生命周期
Activity启动流程总结-超时判定机制

在Activity启动流程中对APP进程和ActivityManagerService的交互过程以及应用进程启动初始化过程做了简单总结。Service作为四大组件之一,它的启动过程大体相似。

概述

在开发中我们使用context.startService(intent)和context.stopService(intent)来启动和停止Service,Service的启动和停止也是由ActivityManagerService来进行调度,本文通过跟踪源码来看看它的启动和停止过程。

源码探究

文中源码基于 Android 10.0

startService过程

当执行context.startService(intent),将开始Service的启动过程:
[ContextImpl#startService]

public ComponentName startService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, false, mUser);
}

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

private ComponentName startServiceCommon(Intent service, boolean requireForeground,
        UserHandle user) {
    try {
        // ···
        // 请求AMS启动Service
        ComponentName cn = ActivityManager.getService().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), requireForeground,
                        getOpPackageName(), user.getIdentifier());
        if (cn != null) {
            // 省略检查异常部分 ···
        }
        return cn;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

可以看到通过IActivityManager调用ActivityManagerService进行startService。

接下来从APP进程来到ActivityManagerService:
[ActivityManagerService#startService]

public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, boolean requireForeground, String callingPackage, int userId)
        throws TransactionTooLargeException {
    enforceNotIsolatedCaller("startService");
    // Refuse possible leaked file descriptors
    // 省略参数异常检查 ···
    synchronized(this) {
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        ComponentName res;
        try {
            // mServices实例是ActiveServices
            res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid,
                    requireForeground, callingPackage, userId);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
        return res;
    }
}

该方法中调用ActiveServices的startServiceLocked方法进一步处理。

ActiveServices的startServiceLocked方法中又调用了另一个重载方法:
[ActiveServices#startServiceLocked]

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, boolean fgRequired, String callingPackage,
        final int userId, boolean allowBackgroundActivityStarts)
        throws TransactionTooLargeException {
    // 省略变量callerFg部分(用于标记调用方进程是否属于前台)···
    
    // 查找或新建可用ServiceRecord
    ServiceLookupResult res =
        retrieveServiceLocked(service, null, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg, false, false);
    // 省略对res结果检查部分 ···
    
    ServiceRecord r = res.record;
    
    // 省略后台启动检查和启动权限检查部分 ···
    
    // 创建startCommand参数添加到pendingStarts集合中保存
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
        service, neededGrants, callingUid));
    
    // 调用startServiceInnerLocked进一步处理
    ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    return cmp;

这里获取一个ServiceRecord(被包装在ServiceLookupResult中),ServiceRecord用于保存Service信息,一个ServiceRecord对应一个运行Service。然后调用startServiceInnerLocked方法进一步执行启动流程。

retrieveServiceLocked方法中,首先获取userId对应的ServiceMap缓存集合,根据ComponentName(显式意图)或IntentFilter(隐式意图)查找ServiceRecord。不存在的话,则从PackageManagerService查找匹配的ServiceInfo,之后创建ServiceRecord并保存在集合中。在返回结果前还会判断是否允许不同应用和不同userId间的调用和启动权限检查。

接下来进入startServiceInnerLocked方法:
[ActiveServices#startServiceInnerLocked]

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
        boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    // ···
    // 进一步执行启动
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
    if (error != null) {
        return new ComponentName("!!", error);
    }
    
    // 省略添加后台启动服务集合和启动前台服务判断部分 ···
    
    return r.name;
}

进入bringUpServiceLocked方法:
[ActiveServices#bringUpServiceLocked]

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting, boolean permissionsReviewRequired)
        throws TransactionTooLargeException {
    // 判断目标Service是否已启动,首次将要启动时ServiceRecord中还未设置IApplicationThread
    if (r.app != null && r.app.thread != null) {
        // 调度应用进程触发onStartCommand回调
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }
    
    // 判断目标Service是否等待重启
    if (!whileRestarting && mRestartingServices.contains(r)) {
        // If waiting for a restart, then do nothing.
        return null;
    }
    
    // ···
    
    // 重置目标Service的延迟启动标识 ···
    // 检查目标Service的userId对应用户是否已启动 ···
    // ···
    
    final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
    final String procName = r.processName;
    HostingRecord hostingRecord = new HostingRecord("service", r.instanceName);
    ProcessRecord app;

    if (!isolated) {
        // 查找对应进程的ProcessRecord
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                    + " app=" + app);
        // 通过ProcessRecord的的IApplicationThread判断进程是否启动
        if (app != null && app.thread != null) {
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                // 进一步执行启动
                realStartServiceLocked(r, app, execInFg);
                return null;
            } catch (TransactionTooLargeException e) {
                throw e;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }
    } else {
        // 省略ISOLATED PROCESS SERVICE部分 ···
    }

    // Not running -- get it started, and enqueue this service record
    // to be executed when the app comes up.
    if (app == null && !permissionsReviewRequired) {
        // 省略启动进程部分,会调用mAm.startProcessLocked方法 ···
    }
    
    // ···
    
    // 将ServiceRecord添加至待启动集合缓存,等待进场启动后再执行启动流程
    if (!mPendingServices.contains(r)) {
        mPendingServices.add(r);
    }
    
    // 省略延迟启动但是Service又被要求停止,会调用stopServiceLocked方法 ···
    
    return null;
}

该方法中首先判断目标Service是否已启动,如果已经启动则会调度onStartCommand回调。接着判断目标Service所属进程是否启动,如果已经启动则进一步执行Service启动流程,否则将ServiceRecord添加到待启动集合,等待目标进程启动完成后再执行启动流程。

PS:这里通过ProcessRecord是否持有IApplicationThread来判断目标Service和目标进程是否启动。当目标Service启动后,会为对应ServiceRecord设置ProcessRecord和IApplicationThread。当目标进程启动后,会为对应ProcessRecord设置IApplicationThread。可参考《Activity启动流程总结-ActivityThread》

接下来进入关键方法realStartServiceLocked:
[ActiveServices#realStartServiceLocked]

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    // ···
    // 这里给ServiceRecord设置了ProcessRecord,便可通过ServiceRecord判断目标Service是否已启动
    r.setProcess(app);
    // ···
    // ProcessRecord的services集合保存对应进程中所有运行的Service信息
    final boolean newService = app.services.add(r);
    // 超时设置相关
    bumpServiceExecutingLocked(r, execInFg, "create");
    // ···
    
    boolean created = false;
    try {
        // 调度应用进程启动Service
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                app.getReportedProcState());
        // 前台Service前台通知相关
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        Slog.w(TAG, "Application dead when creating service " + r);
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        if (!created) {
            // 省略Service启动失败异常处理部分 ···
        }
    }
    
    // ···
    // 省略bind Service相关流程部分 ···
    // ···
    
    // 发送参数到应用进程,将触发onStartCommand回调
    sendServiceArgsLocked(r, execInFg, true);
    
    // ···
}

这里会先后两次调度应用进程,应用侧会先后往应用主进程消息队列添加两条任务消息,AMS侧继续往下执行它的其余逻辑,下面需要到应用侧看创建Service的过程。

在看create Service流程前,先看看sendServiceArgsLocked方法:
[ActiveServices#sendServiceArgsLocked]

private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
        boolean oomAdjusted) throws TransactionTooLargeException {
    // pendingStarts保存待派发执行startCommand的命令参数
    final int N = r.pendingStarts.size();
    if (N == 0) {
        return;
    }

    ArrayList<ServiceStartArgs> args = new ArrayList<>();
    
    while (r.pendingStarts.size() > 0) {
        // 封装参数保存在args中 ···
    }

    // ParceledListSlice用于跨IPC传输大量Parcelable对象,可拆分为多个binder_transact事务传输。
    ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
    slice.setInlineCountLimit(4);
    Exception caughtException = null;
    try {
        // 调度应用进程执行onStartCommand回调
        r.app.thread.scheduleServiceArgs(r, slice);
    } catch (TransactionTooLargeException e) {
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
                + " args, first: " + args.get(0).args);
        Slog.w(TAG, "Failed delivering service starts", e);
        caughtException = e;
    } catch (RemoteException e) {
        // Remote process gone...  we'll let the normal cleanup take care of this.
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
        Slog.w(TAG, "Failed delivering service starts", e);
        caughtException = e;
    } catch (Exception e) {
        Slog.w(TAG, "Unexpected exception", e);
        caughtException = e;
    }
    
    if (caughtException != null) {
        // Keep nesting count correct
        // 省略异常处理部分 ···
    }
}

该方法中进行封装批量StartCommand参数,然后调度应用进程执行scheduleServiceArgs方法。

接下来在应用侧将会先后执行Service的create和start两个消息任务。

onCreate阶段

app.thread.scheduleCreateService将执行应用进程ActivityThread的scheduleCreateService方法,该方法中会发生H.CREATE_SERVICE消息至主线程,将执行handleCreateService方法

[ActivityThread#handleCreateService]

private void handleCreateService(CreateServiceData data) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    // 移除请求GC的消息
    unscheduleGcIdler();

    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        // 通过反射实例化目标Service
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
    } catch (Exception e) {
        // 省略异常处理 ···
    }

    try {
        if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

        // 创建上下文ContextImpl
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        // context持有service实例
        context.setOuterContext(service);

        // 获取应用进程Application实例
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        // service中保存传入的参数,会调用attachBaseContext方法使mBase持有ContextImpl
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        // 调用Service的onCreate生命周期回调方法
        service.onCreate();
        // 以token为key,添加service实例至集合缓存(token是AMS传来的ServiceRecord客户端代理,ServiceRecord继承自Binder)
        mServices.put(data.token, service);
        try {
            // 通知AMS创建完成
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (Exception e) {
        // 省略异常处理 ···
    }
}

首先通过反射创建目标Service,之后创建ContextImpl并使ContextImpl和Service互相持有对方引用,获取应用Application实例并使Service持有其引用。接着便调用onCreate生命周期回调方法。最后再通知AMS创建完成。

再来到ActivityManagerService,在它的serviceDoneExecuting方法中又调用了ActiveServices的serviceDoneExecutingLocked方法:
[ActiveServices#serviceDoneExecutingLocked]

void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
    // mDestroyingServices保存执行销毁的Service
    boolean inDestroying = mDestroyingServices.contains(r);
    if (r != null) {
        // 判断type类型。
        // 刚create时是SERVICE_DONE_EXECUTING_ANON,startCommand时是SERVICE_DONE_EXECUTING_START,stop时是SERVICE_DONE_EXECUTING_STOP。
        if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
            // 设置ServiceRecord相关参数,主要设置stopIfKilled(异常杀死是否不能够重启) ···
        } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) {
            // ···
        }
        final long origId = Binder.clearCallingIdentity();
        // 更新ServiceRecord中的状态,移除超时消息
        serviceDoneExecutingLocked(r, inDestroying, inDestroying);
        Binder.restoreCallingIdentity(origId);
    } else {
        Slog.w(TAG, "Done executing unknown service from pid "
                + Binder.getCallingPid());
    }

该方法主要是处理ServiceRecord中的参数和状态更新设置,以及移除超时消息。

onStartCommand阶段

app.thread.scheduleServiceArgs将会执行ActivityThread的scheduleServiceArgs方法:
[ActivityThread#scheduleServiceArgs]

public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
    List<ServiceStartArgs> list = args.getList();

    for (int i = 0; i < list.size(); i++) {
        // 依次取出参数,发送到主线程执行
        ServiceStartArgs ssa = list.get(i);
        ServiceArgsData s = new ServiceArgsData();
        s.token = token;
        s.taskRemoved = ssa.taskRemoved;
        s.startId = ssa.startId;
        s.flags = ssa.flags;
        s.args = ssa.args;

        sendMessage(H.SERVICE_ARGS, s);
    }
}

H.SERVICE_ARGS消息将会执行handleServiceArgs方法:
[ActivityThread#handleServiceArgs]

private void handleServiceArgs(ServiceArgsData data) {
    // 取出create阶段创建并缓存的Service实例
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            if (data.args != null) {
                // 给Intent设置ClassLoader
                data.args.setExtrasClassLoader(s.getClassLoader());
                data.args.prepareToEnterProcess();
            }
            int res;
            if (!data.taskRemoved) {
                // 触发Service的onStartCommand生命周期回调
                res = s.onStartCommand(data.args, data.flags, data.startId);
            } else {
                s.onTaskRemoved(data.args);
                res = Service.START_TASK_REMOVED_COMPLETE;
            }

            // 这里会阻塞当前线程,直到QueuedWork中的Runnable执行完毕。(SharedPreference会用到)
            QueuedWork.waitToFinish();

            try {
                // 通知AMS执行完成
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(s, e)) {
                throw new RuntimeException(
                        "Unable to start service " + s
                        + " with " + data.args + ": " + e.toString(), e);
            }
        }
    }
}

这里取出缓存的Service实例,然后便调用其onStartCommand生命周期回调方法。最后也会通知AMS进行状态更新操作。

到这里,Service的启动流程便完成,先后经过了onCreate和onStartCommand生命周期回调。

Service超时机制

在Service启动过程中,AMS会进行超时机制相关设置,包括设置超时时间、触发超时处理、移除超时处理。

设置超时

回到ActiveServices#realStartServiceLocked方法中,在调度应用进程创建Service前,会先执行bumpServiceExecutingLocked方法,在该方法中会调用scheduleServiceTimeoutLocked方法进行超时监听的设置。

[ActiveServices#scheduleServiceTimeoutLocked]

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return;
    }
    // 获取SERVICE_TIMEOUT_MSG消息
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    // 发送延迟消息
    mAm.mHandler.sendMessageDelayed(msg,
            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

该方法中发送一个延迟消息SERVICE_TIMEOUT_MSG,当20s(启动前台服务)或200s(启动后台服务)后仍没有执行完,则会触发超时处理。

移除超时

当应用进程侧完成Service启动后,便会调用AMS侧的ActiveServices#serviceDoneExecutingLocked方法,其中会进行超时消息的移除:
[ActiveServices#serviceDoneExecutingLocked]

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
        boolean finishing) {
    // ···
    // 移除SERVICE_TIMEOUT_MSG消息
    mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
    // ···
}
触发超时

若在规定时间内,应用侧没有及时完成启动流程和通知AMS,则会触发超时逻辑,将会执行ActiveServices的serviceTimeout方法。

[ActiveServices#serviceTimeout]

void serviceTimeout(ProcessRecord proc) {
    // ···
    if (anrMessage != null) {
        // dump ANR信息和触发ANR弹窗
        proc.appNotResponding(null, null, null, null, false, anrMessage);
    }
}

当触发超时后,将会触发ANR。

当目标Service进程未启动时

在前面的ActiveServices#bringUpServiceLocked方法中,会首先判断若目标Service进程尚未已经启动,则会先启动应用进程,然后将ServiceRecord保存在mPendingServices集合中

当应用进程启动后,会调用ActivityManagerService的attachApplicationLocked方法,在该方法中会执行待启动的Service。
[ActivityManagerService#attachApplicationLocked]

private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    // ···
    // thread.bindApplication
    // ···
    // Find any services that should be running in this process...
    if (!badApp) {
        try {
            // 启动等待对应进程启动的Service
            didSomething |= mServices.attachApplicationLocked(app, processName);
            checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
        } catch (Exception e) {
            Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
            badApp = true;
        }
    }
    // ···
}

调用了ActiveServices的attachApplicationLocked方法:
[ActiveServices#attachApplicationLocked]

boolean attachApplicationLocked(ProcessRecord proc, String processName)
        throws RemoteException {
    boolean didSomething = false;
    // Collect any services that are waiting for this process to come up.
    if (mPendingServices.size() > 0) {
        ServiceRecord sr = null;
        try {
            // 遍历待启动ServiceRecord
            for (int i=0; i<mPendingServices.size(); i++) {
                sr = mPendingServices.get(i);
                if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
                        || !processName.equals(sr.processName))) {
                    continue;
                }

                mPendingServices.remove(i);
                i--;
                proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode,
                        mAm.mProcessStats);
                // 执行realStartServiceLocked进行启动流程
                realStartServiceLocked(sr, proc, sr.createdFromFg);
                didSomething = true;
                if (!isServiceNeededLocked(sr, false, false)) {
                    // We were waiting for this service to start, but it is actually no
                    // longer needed.  This could happen because bringDownServiceIfNeeded
                    // won't bring down a service that is pending...  so now the pending
                    // is done, so let's drop it.
                    bringDownServiceLocked(sr);
                }
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception in new application when starting service "
                    + sr.shortInstanceName, e);
            throw e;
        }
    }
    // Also, if there are any services that are waiting to restart and
    // would run in this process, now is a good time to start them.  It would
    // be weird to bring up the process but arbitrarily not let the services
    // run at this point just because their restart time hasn't come up.
    if (mRestartingServices.size() > 0) {
        // 省略待重启Service部分 ···
    }
    return didSomething;
}

可以看到,当应用进程启动完成通知AMS后,会再进行Service启动流程。

stopService过程

停止Service通过调用context.stopService方法:
[ContextImpl#stopService]

public boolean stopService(Intent service) {
    warnIfCallingFromSystemProcess();
    return stopServiceCommon(service, mUser);
}

private boolean stopServiceCommon(Intent service, UserHandle user) {
    try {
        // ···
        // 请求AMS执行stopService调度
        int res = ActivityManager.getService().stopService(
            mMainThread.getApplicationThread(), service,
            service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
        // ···
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

和startService一样,也是调用ActivityManagerService的方法,在stopService中又会调用ActiveServices的stopServiceLocked方法:
[ActiveServices#stopServiceLocked]

int stopServiceLocked(IApplicationThread caller, Intent service,
        String resolvedType, int userId) {
    // ···
    // If this service is active, make sure it is stopped.
    ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, null,
            Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false, false);
    if (r != null) {
        if (r.record != null) {
            final long origId = Binder.clearCallingIdentity();
            try {
                stopServiceLocked(r.record);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return 1;
        }
        return -1;
    }

    return 0;
}

首先查找目标ServiceRecord,然后调用stopServiceLocked方法。

stopServiceLocked方法最终会调用bringDownServiceLocked方法:
[ActiveServices#bringDownServiceLocked]

private final void bringDownServiceLocked(ServiceRecord r) {
    // ···
    
    if (r.app != null) {
        if (r.app.thread != null) {
            try {
                // ···
                r.app.thread.scheduleStopService(r);
            } catch (Exception e) {
                // ···
            }
        } else {
            // ···
        }
    } else {
        // ···
    }
    
    // ···
}

调度应用进程执行stop操作。

scheduleStopService将发送H.STOP_SERVICE消息,最终将执行handleStopService方法:
[ActivityThread#handleStopService]

private void handleStopService(IBinder token) {
    // 获取缓存的Service实例
    Service s = mServices.remove(token);
    if (s != null) {
        try {
            if (localLOGV) Slog.v(TAG, "Destroying service " + s);
            // 执行onDestroy回调
            s.onDestroy();
            s.detachAndCleanUp();
            Context context = s.getBaseContext();
            if (context instanceof ContextImpl) {
                final String who = s.getClassName();
                ((ContextImpl) context).scheduleFinalCleanup(who, "Service");
            }

            QueuedWork.waitToFinish();

            try {
                // 通知AMS
                ActivityManager.getService().serviceDoneExecuting(
                        token, SERVICE_DONE_EXECUTING_STOP, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            // ···
        }
    } else {
        Slog.i(TAG, "handleStopService: token=" + token + " not found.");
    }
    //Slog.i(TAG, "Running services: " + mServices);
}

应用侧stopService就是从mServices集合中移除Service实例,并执行它的onDestroy生命周期回调方法,最后通知AMS。

总结

Service的启动和停止都是通过发请求给ActivityManagerService,由AMS来进行调度分发。AMS侧通过ServiceRecord记录运行Service的信息,应用侧通过mServices集合保存反射创建的Service实例。AMS发送启动或停止指令到应用侧后,由应用侧对Service实例执行对应的生命周期回调。

下面用时序图总结这两个流程。
启动流程:

启动service

停止流程:

停止service

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Service启动流程总结-start和stop service 的相关文章

  • 如何获取之前的碎片?

    为了在我的应用程序中重用某些片段 我需要知道哪个片段是返回堆栈上的第二个片段 为了做到这一点 我正在使用getFragmentManager getFragments 显示以下错误 但有效 FragmentManager getFragme
  • 在android中通过BLE传输图像

    我使用以下代码传输 1 MB 的图像 如果在每个数据包之间实现线程延迟 则图像将成功传输 如果未设置线程延迟 则所有数据包均从BluetoothGattServer 发送 但BluetoothGattCallback 不会接收所有数据包 任
  • FTS3 在 ORMLite 中搜索?

    我对 FTS3 一无所知 除了http developer android com guide topics search search dialog html http developer android com guide topics
  • 如何使用Android opencv使图像的白色部分透明

    我无法链接超过 2 个网址 因此我将我的照片发布到此博客 请在这里查看我的问题 http blog naver com mail1001 220650041897 http blog naver com mail1001 220650041
  • 安卓定位不准确

    我正在尝试获取当前用户的位置 我试图重构我的代码以获得更好的结果 但我只是不断得到关于准确度的荒谬位置 它在 900 600 米之间 如何才能得到更好的结果 使其精度达到50m以内 这是我的代码 package com agam mapsl
  • 调试:在 Android 1.0 中找不到文件

    今天我更新到 Android Studio v 1 0 在尝试编译任何项目时出现以下错误 app build intermediates classes debug 找不到文件 问题是在更新之前我没有任何问题 这是我实际尝试编译的代码 构建
  • Android在排序列表时忽略大小写

    我有一个名为路径的列表 我目前正在使用以下代码对字符串进行排序 java util Collections sort path 这工作正常 它对我的 列表进行排序 但是它以不同的方式处理第一个字母的情况 即它用大写字母对列表进行排序 然后用
  • AudioTrack、SoundPool 或 MediaPlayer,我应该使用哪个?

    如果我需要能够 播放多个音频文件 具有不同的持续时间 例如 5 到 30 秒 独立设置右 左声道的音量 应用声音效果 如混响 失真 那么 我应该使用哪个 API 另外 我在 AudioTrack API 上找不到太多文档 有谁知道在哪里可以
  • 如何在手机缓存中保存用户名和密码

    我有一个用户登录应用程序 它需要用户的电子邮件和密码 我想将电子邮件和密码保存在手机缓存中 以便用户不必再次输入 有什么办法可以将数据保存在Android缓存中吗 我认为你需要使用SharedPreference用于在设备中使用应用程序时保
  • 放置在 NavigationDrawer 顶部的片段

    我正在尝试添加一个PreferenceFragment在我的应用程序中 问题是 它自动放置在我的顶部NavigationDrawer public class SetPreferenceActivity extends Activity O
  • 使用 gradlew assembleRelease 从 React Native 创建发布 apk 时出现错误

    我想发布 apk 但我收到错误 文件已存在 mkdir D mobile 它在 d 驱动器中生成名为 mobile 的文件 删除文件后 再次执行 gradlew assembleRelease 创建该文件并抛出错误 任务 app bundl
  • 如何在 Android 模块中使用 FirebaseAuth

    我正在开发一个聊天库 我想在其中显示登录用户的对话 制作该库的原因是我想将其集成到多个项目中 我现在面临的问题是FirebaseAuth表示用户尚未登录 FirebaseAuth getInstance mFirebaseApp getCu
  • 使用 PhoneGap 使 Android 应用程序易于访问(对于残障人士)

    有人有过使用 PhoneGap 使 Android 应用程序可访问的经验吗 至少我们需要使我们的应用程序符合第 508 条规定 我尝试实现一些标准的辅助功能 文本框标签 向 div 添加标题属性等 但是 当在 Android 中使用 Tal
  • Android FragmentTransaction 自定义动画(未知动画师名称:Translate)

    我正在尝试让自定义动画与我的片段一起使用 我已按照在线教程进行操作 但出现以下错误 java lang RuntimeException 未知的动画师名称 翻译 动画的 XML 如下
  • 有关 ListView 自定义行布局项目上的 onClick() 事件的帮助

    我有一个 ListView 其行由我格式化 每行都有 ImageView 和 TextView 的混合 我还实现了自己的适配器 并且能够通过它绘制每一行 现在 我想要这样的东西 用户单击 ImageView 不是行上的其他任何位置 但只有此
  • 模块中的类无法加载

    我正在开发一个 2D Unity android 游戏 其中我最近添加了 Firebase Beta SDK 但添加后FirebaseAnalytics unitypackage我面临的错误是 无法加载模块中的类 当我删除文件夹时Fireb
  • 在 KitKat 4.4.2 中获取 SDard 路径和大小

    我在 Google Play 上有一个设备信息应用程序 在该应用程序中我有存储信息 我知道 Android 4 4 在访问外部 SD 卡方面发生了一些变化 内部似乎没有给我带来问题 我的问题是 如何可靠地获取 KitKat 上 SD 卡的大
  • Android应用程序可以像旧的普通java小程序一样嵌入到网页中吗?

    我对 android 平台一无所知 也无法在互联网上找到这个基本问题的答案 更新 好的 我本身无法嵌入 Android 应用程序 但是我可以在 Android Webbrowser 中嵌入 Java 的东西吗 不可以 您无法将 Androi
  • Android 中带有组的列表视图

    我有一个列表视图 每行都有一些日期和文本 我可以像 iPhone 中那样将这个 listView 分组 组之间有标题吗 在 android 中是否可能 请帮忙 即 我需要在 Listview 行之间有标题栏 以便如果我使用日期对其进行分组
  • 如何在片段中实现 onBackPressed() 和意图?

    我知道 onBackPressed 是活动中的一种方法 但是 我想在片段中使用该功能 以便当按下后退按钮时 它会通过 Intent 重定向到另一个活动 有什么办法解决这个问题吗 public class News Events fragme

随机推荐

  • 理解CPU/寄存器/内存之间的关系

    转自 https blog csdn net qq 27689785 article details 82975575 CPU 寄存器 内存 因为要了解多线程 自然少不了一些硬件知识的科普 我没有系统学习过硬件知识 仅仅是从书上以及网络上看
  • Node.js全栈开发笔记与心得

    highlight a11y dark 一 Node js 全栈开发资料 1 前端入门基础 慕课网HTML CSS入门 慕课网JS入门 javascript进阶篇 菜鸟教程html部分 菜鸟教程CSS部分 阮一峰js入门 阮一峰es6教程
  • 一道有关逆序对的算法题---归并、树状数组、线段树三种解法

    这阵子巨忙 呜呜呜 上周末打了第十四届蓝桥杯的校内赛 总的来说 题目是很简单的 不过其中的压轴题挺有意思 所以我打算来一发博客记录记录这道算法题 废话不多说了 上题 问题描述 小蓝有一个序列 a 1 a 2 a n 每次可以交换相邻的两个元
  • 服务器系统网口,服务器网口设置方法

    服务器网口设置方法 内容精选 换一换 介绍常见的安全组配置示例 如下示例中 出方向默认全通 仅介绍入方向规则配置方法 不同安全组内的云耀云服务器内网互通仅允许特定 IP 地址远程连接云耀云服务器SSH远程连接Linux云耀云服务器RDP远程
  • 数据库原理-ER模型转换为关系模型

    1 ER图转换成关系模式集的算法 联系类型的转换 不同的情况做不同的处理 二元联系类型的转换 若实体间联系是1 1 可以在两个实体类型转换成的两个关系模式中任意一个关系模式的属性中加入另一个关系模式的键和联系类型的属性 若实体间联系是1 n
  • pandas dataframe 新增单列和多列

    dataframe 新增单列 assign方法 dataframe assign方法 返回一个新对象 副本 不影响旧dataframe对象 import pandas as pd df pd DataFrame col 1 0 1 2 3
  • C/C++混用的线性表基本操作(Five的期末复习,part1)

    一 线性表的定义 线性表 零个或多个数据元素的有限序列 几个关键的地方 首先它是一个序列 也就是说 元素之间是有顺序的 若元素存在多个 则第一个元素无前驱 最后一个元素无后继 其他每个元素都只有一个前驱和后继 然后 线性表强调是有限的 事实
  • tensorflow的 jupyter环境 error :UnrecognizedFlagError: Unknown command line flag 'f' 解决办法

    加一行代码 tf app flags DEFINE string f kernel
  • VS2017+DirectX9环境配置

    正好学完浅墨大神的游戏编程著作 来写一下VS2017怎么配置DirectX9 首先去下一个DX9的SDK SDK下载 然后找地方安装上 不过可能安装会出问题 我的情况解决方式如下 去控制面板里面卸载掉这两个东西 然后再安装下载下来的DXSD
  • ogre 1.7.0

    今天编译了orge 第一次安装编译orge挺麻烦的 现把编译和安装步骤总结一下 1 ogre 1 7 0的下载地址 https sourceforge net projects ogre files ogre 1 7 ogre v1 7 0
  • Python之赋值、深拷贝与浅拷贝

    一图胜千言 先上三幅小图 图片来源 赋值操作 浅拷贝操作 深拷贝操作 赋值操作 赋值是将一个对象指向一个已经存在的对象的过程 通常使用 texttt 来完成赋值操作
  • pd.DataFrame()函数解析

    文章目录 DataFrame是Python中Pandas库中的一种数据结构 它类似excel 是一种二维表 0 简介 DataFrame的单元格可以存放数值 字符串等 这和excel表很像 同时DataFrame可以设置列名columns与
  • Qt打开外部文件/文件夹

    方法一 QDesktopServices 1 函数原型 头文件 include
  • idea2021.3.3的安装方式以及使用方法

    1 打开idea的官网 可以点击下方链接进行下载 该软件版权为Jetbrains所有 我们仅作使用 欢迎大家使用正版软件 下面是idea的官方网址 idea的直接链接https www jetbrains com idea 打开链接之后 点
  • python聚类分析顾客_Python用K-means聚类算法进行客户分群的实现

    一 背景 1 项目描述 你拥有一个超市 Supermarket Mall 通过会员卡 你用有一些关于你的客户的基本数据 如客户ID 年龄 性别 年收入和消费分数 消费分数是根据客户行为和购买数据等定义的参数分配给客户的 问题陈述 你拥有这个
  • 使用winserver2003配置DNS服务器

    实验准备 Windows server 2003 R2 虚拟机一台 win 7 客户机一台 DNS内容 dns domain name service 域名解析服务 域名 例如 www baidu com 中 baidu com 是域名 w
  • Electron 入门案例详解

    目录 前言 一 开发环境检查 二 入门案例实现 1 初始化项目 2 安装electron包 3 运行electron应用 三 创建第一个应用窗口 1 准备页面 2 创建窗口 3 运行窗口 总结 前言 Electron 是一种基于 Node
  • 史上最全零拷贝总结

    一 传统方式下消息读取发送 很多应用程序在发送文件时 可以等价为进行如下的系统调用 File read file buf len Socket send socket buf len 例如消息中间件 Kafka 就是这个应用场景 从磁盘中读
  • 凸透镜成像动画可拖动_重要实验——凸透镜成像

    一 基本概念 透镜 透镜就是用透明物质制成的表面为球面的一部分的光学元件 透镜的光心 如上图所示 透镜的几何中心O一般称为透镜的光心 透镜的主光轴 如上图所示 经过透镜光心的一条水平直线称为透镜的主光轴 透镜的焦点 以凸透镜为例 如上图所示
  • Service启动流程总结-start和stop service

    文章目录 回顾 概述 源码探究 startService过程 onCreate阶段 onStartCommand阶段 Service超时机制 设置超时 移除超时 触发超时 当目标Service进程未启动时 stopService过程 总结