Service重启流程

本文档适合对Service的启动和停止的fwk流程有一定了解的查看。

基础知识

ServiceRecord


    boolean fgRequired;    // 是否请求的是前台Service,完成后设置为false。

    boolean startRequested; //  是否是start方式启动,在start权限校验后才会赋值为true,Service stop后会赋值为false

    boolean isForeground;  // 当前Service是否是前台Service,startForeground后赋值为true,stopForeground赋值为false

    boolean stopIfKilled;  // 跟onStartCommand返回值有关,START_STICKY_COMPATIBILITY/START_STICKY等为false;表示Service被杀后是否停止重启;

    boolean callStart;      //  决定Service重启后是否执行onStartCommand,START_STICKY_COMPATIBILITY时会赋值为false

    int crashCount;        // number of times proc has crashed with service running

    int restartCount;      // 连续执行的重启次数

    long restartDelay;      // 下一次重启的delay,scheduleServiceRestartLocked 赋值,启动/停止/绑定时会重置为0

    long restartTime;      //上次调用realStartServiceLocked的时间

    long nextRestartTime;  // 当前时间+restartDelay

    int totalRestartCount;  // 不得不重启的次数

    final ArrayList deliveredStarts = new ArrayList(); // 在sendServiceArgsLocked中会添加StartItem到这个列表中,执行完serviceDoneExecutingLocked/或stop后会从该列表中移除,返回值为START_REDELIVER_INTENT时不会从列表中移除.

    final ArrayList pendingStarts = new ArrayList(); // startServiceInnerLocked时添加进列表,sendServiceArgsLocked 中移除;

StartItem


        long deliveredTime; // 在sendServiceArgsLocked中赋值,START_REDELIVER_INTENT返回值时,重启时会根据这个计算延时时间

        int deliveryCount;  // 调用一次 sendServiceArgsLocked增加一次计数,如果onStartCommand执行失败的次数超过两次,后面就不会为这个intent重发,主要针对START_REDELIVER_INTENT

        int doneExecutingCount;  //如果返回START_REDELIVER_INTENT时,则该引用计数+1

Service重启时序图

常见问题问答

image

什么情况下进程被杀后Service能重启?

  1. start方式启动时onStartCommand返回值是START_STICKY_COMPATIBILITY/START_STICKY/START_REDELIVER_INTENT之一

  2. Servive有flag是BIND_AUTO_CREATE的连接

  3. 进程被杀时Service还没执行完onStartCommand方法,导致deliverStarts中的额StartItem没有被移除


    boolean canStopIfKilled(boolean isStartCanceled) {

        // onStartCommand返回值是START_STICKY_COMPATIBILITY/START_STICKY时,stopIfKilled为false;

        //  onStartCommand返回值是START_REDELIVER_INTENT时,这里的pendingStarts不为空

        return startRequested && (stopIfKilled || isStartCanceled) && pendingStarts.isEmpty();

    }

// Servive有flag是BIND_AUTO_CREATE的连接

    public boolean hasAutoCreateConnections() {

        for (int conni=connections.size()-1; conni>=0; conni--) {

            ArrayList cr = connections.valueAt(conni);

            for (int i=0; i

    /** @return {@code true} if the restart is scheduled. */

    private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {

            .......

            if (allowCancel) {

                // start方式启动,返回值是START_STICKY_COMPATIBILITY/START_STICKY/START_REDELIVER_INTENT之一时,shouldStop为false

                final boolean shouldStop = r.canStopIfKilled(canceled);

                if (shouldStop && !r.hasAutoCreateConnections()) {

                    // Nothing to restart.

                    return false;

                }

                reason = (r.startRequested && !shouldStop) ? "start-requested" : "connection";

            } else {

                reason = "always";

            }

      .......

        return true;

    }

onStartCommand不同返回值有什么不同的效果?


@IntDef(flag = false, prefix = { "START_" }, value = {

        START_STICKY_COMPATIBILITY, // 重启后只执行onCreate

        START_STICKY, // 重启后执行onCreate、onStartCommand,但intent为null

        START_NOT_STICKY, // 不重启

        START_REDELIVER_INTENT, //重启后执行onCreate、onStartCommand、intent不为null、onStartCommand可能执行多次,重启delay时间较长。

})

START_NOT_STICKY

进程被杀后Service不会被重启 : 因为这里stopIfKilled赋值为true。


    void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,

            boolean enqueueOomAdj) {

        boolean inDestroying = mDestroyingServices.contains(r);

        if (r != null) {

            if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {

                // This is a call from a service start...  take care of

                // book-keeping.

                // 表明默认重启后需要回调onStartCommand方法

                r.callStart = true;

                switch (res) {

                  ......

                    case Service.START_NOT_STICKY: {

                        // 查找并将当前StartItem从deliveredStarts列表中移除

                        r.findDeliveredStart(startId, false, true);

                        if (r.getLastStartId() == startId) {

                            // There is no more work, and this service

                            // doesn't want to hang around if killed.

                            r.stopIfKilled = true;

                        }

                        break;

                    }

            ......

            }.......

    }

START_STICKY_COMPATIBILITY

Service被杀死后会重启(执行了onCreate方法),但是onStartCommand方法没有被执行。


system_process W/ActivityManager: Scheduling restart of crashed service com.example.myapplication/.MyService in 1000ms for start-requested

system_process I/ActivityManager: Start proc 7675:com.example.myapplication/u0a270 for service {com.example.myapplication/com.example.myapplication.MyService} caller=com.example.myapplication

com.example.myapplication D/MyService: onCreate


    void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,

            boolean enqueueOomAdj) {

        boolean inDestroying = mDestroyingServices.contains(r);

        if (r != null) {

            if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {

                // This is a call from a service start...  take care of

                // book-keeping.

                r.callStart = true;

                switch (res) {

                    case Service.START_STICKY_COMPATIBILITY:

                    case Service.START_STICKY: {

                        // We are done with the associated start arguments.

                        // 查找并将当前StartItem从deliveredStarts列表中移除

                        r.findDeliveredStart(startId, false, true);

                        // Don't stop if killed.

                        r.stopIfKilled = false;

                        break;

                    }

                .......

                }

                // 这里callStart赋值为false, 决定Service重启后不执行onStartCommand方法。

                if (res == Service.START_STICKY_COMPATIBILITY) {

                    r.callStart = false;

                }

            }.......

    }


    private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,

            IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,

            boolean enqueueOomAdj) throws RemoteException {

        ......

        // callStart为false,则StartItem不会添加到pendingStarts列表;sendServiceArgsLocked方法中传递到app端的ServiceStartArgs列表会为空,导致不会遍历执行到onStartCommand方法

        if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {

            r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),

                    null, null, 0));

        }

        // 从这个方法中会触发app端回调Service的onStartCommand方法

        sendServiceArgsLocked(r, execInFg, true);

        .......

    }

START_STICKY

进程被杀后Service会被重启,执行onCreate和onStartCommand,重启后的Intent为null。

具体代码同START_STICKY_COMPATIBILITY。


com.example.myapplication D/MyService: onCreate

com.example.myapplication D/MyService: onStartCommand intent=Intent { cmp=com.example.myapplication/.MyService (has extras) } startId=1

com.example.myapplication D/MyService: startResult= 1

system_process W/ActivityManager: Scheduling restart of crashed service com.example.myapplication/.MyService in 1000ms for start-requested

system_process I/ActivityManager: Start proc 4522:com.example.myapplication/u0a270 for service {com.example.myapplication/com.example.myapplication.MyService} caller=com.example.myapplication

com.example.myapplication D/MyService: onCreate

com.example.myapplication D/MyService: onStartCommand intent=null startId=2 // startID不一致,代表不是同一个实例,intent为null

com.example.myapplication D/MyService: startResult= 1

START_REDELIVER_INTENT

Service被杀死后会重启(执行了onCreate方法),并且onStartCommand方法会执行。从startId可以看出两个Service对象是同一个,并且onStartCommand方法中的intent和前一个intent值是一致的。


com.example.myapplication D/MyService: onCreate

com.example.myapplication D/MyService: onStartCommand intent=Intent { cmp=com.example.myapplication/.MyService (has extras) } startId=1

com.example.myapplication D/MyService: startResult= 3

system_process W/ActivityManager: Scheduling restart of crashed service com.example.myapplication/.MyService in 17938ms for start-requested

system_process I/ActivityManager: Start proc 6307:com.example.myapplication/u0a270 for service {com.example.myapplication/com.example.myapplication.MyService} caller=com.example.myapplication

com.example.myapplication D/MyService: onCreate

com.example.myapplication D/MyService: onStartCommand intent=Intent { cmp=com.example.myapplication/.MyService (has extras) } startId=1

com.example.myapplication D/MyService: startResult= 3


    void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,

            boolean enqueueOomAdj) {

        boolean inDestroying = mDestroyingServices.contains(r);

        if (r != null) {

            if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {

                // This is a call from a service start...  take care of

                // book-keeping.

                r.callStart = true;

                switch (res) {

                  ......

                    case Service.START_REDELIVER_INTENT: {锁

                      // 这个返回值,执行完Service的onStartCommand方法后,不会从deliverStarts列表中移除当前的StartItem,Service重启时会遍历deliverStarts列表,并将该列表中item添加到pendingStarts

                        ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);

                        if (si != null) {

                            // 赋值为0,非0的话则表示app端的onStartCommand方法未正常执行

                            si.deliveryCount = 0;

                            // 标明该StartItem的执行成功的次数

                            si.doneExecutingCount++;

                            r.stopIfKilled = true;

                        }

                        break;

                    }

              .......

            }.......

    }


    private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {

      .......

          // 非persistent app才会判断重不重启,persistent app直接重启

        if ((r.serviceInfo.applicationInfo.flags

                &ApplicationInfo.FLAG_PERSISTENT) == 0) {

        ......

            final int N = r.deliveredStarts.size();

            // 针对START_REDELIVER_INTENT返回值,deliveredStarts不为空; 也有可能是进程被杀后onStartCommand方法还没执行完。

            if (N > 0) {

                for (int i=N-1; i>=0; i--) {

                    ServiceRecord.StartItem si = r.deliveredStarts.get(i);

                    si.removeUriPermissionsLocked();

                    if (si.intent == null) {

                        // We'll generate this again if needed.

                        // 同一个StartItem重启失败<3次或成功<6次才会继续重启

                    } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT //3

                            && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) { //6

                        // 将列表中item添加到pendingStarts

                        r.pendingStarts.add(0, si);

                        long dur = SystemClock.uptimeMillis() - si.deliveredTime;

                        dur *= 2;

                        // minDuration和resetTime赋值,一般是当前时间到deliver的时间间隔(下面称作devliver Duration),所以重启delay时间会很长,超过1s。

                        if (minDuration < dur) minDuration = dur;

                        if (resetTime < dur) resetTime = dur;

                    } else {

                        // 否则直接取消重启

                        Slog.w(TAG, "Canceling start item " + si.intent + " in service "

                                + r.shortInstanceName);

                        canceled = true;

                    }

                }

                // 清空Service的deliveredStarts的列表

                r.deliveredStarts.clear();

            }

Bind情况下如何重启?

Service有BIND_AUTO_CREATE的连接


com.example.testapplication D/MainActivity: flags= 1

com.example.myapplication D/MyService2: onCreate

com.example.myapplication D/MyService2: onBind

com.example.testapplication D/MainActivity: onServiceConnected

com.example.testapplication D/MainActivity: onServiceDisconnected

ActivityManager: Scheduling restart of crashed service com.example.myapplication/.MyService2 in 10043ms for connection

ActivityManager: Start proc 10102:com.example.myapplication:remote/u0a253 for service {com.example.myapplication/com.example.myapplication.MyService2} caller=com.example.testapplication

com.example.myapplication D/MyService2: onCreate

com.example.myapplication D/MyService2: onBind

com.example.testapplication D/MainActivity: onServiceConnected

先bindService再startService


com.example.myapplication D/MyService2: onCreate

com.example.myapplication D/MyService2: onBind

com.example.myapplication D/MyService2: onStartCommand

com.example.testapplication D/MainActivity: onServiceConnected

com.example.testapplication D/MainActivity: onServiceDisconnected

system_process W/ActivityManager: Scheduling restart of crashed service com.example.myapplication/.MyService2 in 1000ms for start-requested

system_process I/ActivityManager: Start proc 13953:com.example.myapplication:remote/u0a253 for service {com.example.myapplication/com.example.myapplication.MyService2} caller=com.example.myapplication

com.example.myapplication D/MyService2: onCreate

com.example.myapplication D/MyService2: onBind

com.example.testapplication D/MainActivity: onServiceConnected

com.example.myapplication D/MyService2: onStartCommand

重启时间如何计算?

首次重启

1. 如果是START_REDELIVER_INTENT返回值,重启delay时间一般为 (当前时间 - 上次deliver时间)*2,这个时间一般较长。

1. 其它重启时间为1s。

app发生过crash

重启delay时间为1800s*(crashCount -1)

非首次重启

1. START_REDELIVER_INTENT返回值, 当前时间距离上次重启 > 60s(或deliverDuration) ,则重启delay一般为deliverDuration;否则重启delay*60s。

1. 其它,当前时间距离上次重启 > 60s,则重启delay为1s,否则delay * 60s。

最后需要保证两个重启的Service时间间隔不得<10s


    /** @return {@code true} if the restart is scheduled. */

    private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {

        ......

        final long now = SystemClock.uptimeMillis();

        final String reason;

        if ((r.serviceInfo.applicationInfo.flags

                &ApplicationInfo.FLAG_PERSISTENT) == 0) {

            long minDuration = mAm.mConstants.SERVICE_RESTART_DURATION; //1s

            long resetTime = mAm.mConstants.SERVICE_RESET_RUN_DURATION; //60s

            boolean canceled = false;

            ........

            // 第一次重启

            if (r.restartDelay == 0) {

                r.restartCount++;

                r.restartDelay = minDuration;

                // crash重启时间为1800s * n

            } else if (r.crashCount > 1) {

                r.restartDelay = mAm.mConstants.BOUND_SERVICE_CRASH_RESTART_DURATION

                        * (r.crashCount - 1);

            } else {

                // 如果这次重启距离上次重启>60s(或者deliverDuration),重置delay为minDuration(非Deliver一般为1s)

                if (now > (r.restartTime+resetTime)) {

                    r.restartCount = 1;

                    r.restartDelay = minDuration;

                } else {

                    // 如果重启后很快又被杀,则delay需要* 60s的系数,增大重启延时

                    r.restartDelay *= mAm.mConstants.SERVICE_RESTART_DURATION_FACTOR;

                    if (r.restartDelay < minDuration) {

                        r.restartDelay = minDuration;

                    }

                }

            }

            if (isServiceRestartBackoffEnabledLocked(r.packageName)) {

                // 计算下次重启时间

                r.nextRestartTime = now + r.restartDelay;

                // Make sure that we don't end up restarting a bunch of services

                // all at the same time.

                boolean repeat;

                final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN; //10s

                do {

                    // 避免一次重启太多的Service,至少保证10s的时间间隔

                    repeat = false;

                    for (int i = mRestartingServices.size() - 1; i >= 0; i--) {

                        final ServiceRecord r2 = mRestartingServices.get(i);

                        if (r2 != r

                                && r.nextRestartTime >= (r2.nextRestartTime - restartTimeBetween)

                                && r.nextRestartTime < (r2.nextRestartTime + restartTimeBetween)) {

                            r.nextRestartTime = r2.nextRestartTime + restartTimeBetween;

                            r.restartDelay = r.nextRestartTime - now;

                            repeat = true;

                            break;

                        }

                    }

                } while (repeat);

            } else {

                // It's been forced to ignore the restart backoff, fix the delay here.

                r.restartDelay = mAm.mConstants.SERVICE_RESTART_DURATION;

                r.nextRestartTime = now + r.restartDelay;

            }

        } else {

            // Persistent processes are immediately restarted, so there is no

            // reason to hold of on restarting their services.

            // persistent app立刻被拉起

            r.totalRestartCount++;

            r.restartCount = 0;

            r.restartDelay = 0;

            r.nextRestartTime = now;

            reason = "persistent";

        }

        if (!mRestartingServices.contains(r)) {

            r.createdFromFg = false;

            mRestartingServices.add(r);

            synchronized (mAm.mProcessStats.mLock) {

                r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);

            }

        }

        cancelForegroundNotificationLocked(r);

        // 开始shedule重启

        performScheduleRestartLocked(r, "Scheduling", reason, SystemClock.uptimeMillis());

        return true;

    }

如何规避非预期的Service重启

背景

一键清理时会先执行removeTask操作,未添加FLAG_STOP_WITH_TASK flag的Service会触发执行一次onStartCommand方法,且在sendServiceArgsLocked中执行完后将新创建的StartItem从pendingStarts列表移动到deliveredStarts列表。这时候再执行杀进程会导致app端未能及时告知system server端执行serviceDoneExecutingLocked方法去移除deliveredStarts中的StartItem,导致进程被杀后还会立刻通过Service拉起来。


10-12 15:52:22.108 2489 3967 I ProcessManager: OneKeyClean: kill com.miui.huanji:backup Adj=100 State=4

10-12 15:52:22.122 2489 3967 I ProcessManager: OneKeyClean: kill com.miui.huanji Adj=50 State=4

10-12 15:52:22.144 2489 7247 W ActivityManager: Scheduling restart of crashed service com.miui.huanji/.backup.BackupService in 1000ms for connection

10-12 15:52:22.292 2489 7443 W ActivityManager: Scheduling restart of crashed service com.miui.huanji/.scanner.ScannerService in 50853ms for start-requested

代码逻辑

image

    void cleanUpServices(int userId, ComponentName component, Intent baseIntent) {

        ArrayList services = new ArrayList<>();

        ArrayMap alls = getServicesLocked(userId);

        for (int i = alls.size() - 1; i >= 0; i--) {

            ServiceRecord sr = alls.valueAt(i);

            if (sr.packageName.equals(component.getPackageName())) {

                services.add(sr);

            }

        }

        // Take care of any running services associated with the app.

        boolean needOomAdj = false;

        for (int i = services.size() - 1; i >= 0; i--) {

            ServiceRecord sr = services.get(i);

            if (sr.startRequested) {

                if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {

                    Slog.i(TAG, "Stopping service " + sr.shortInstanceName + ": remove task");

                    needOomAdj = true;

                    stopServiceLocked(sr, true);

                } else {

                    // 让app端触发onTaskRemoved方法

                    sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,

                            sr.getLastStartId(), baseIntent, null, 0));

                    if (sr.app != null && sr.app.getThread() != null) {

                        // We always run in the foreground, since this is called as

                        // part of the "remove task" UI operation.

                        try {

                            sendServiceArgsLocked(sr, true, false);

                        } catch (TransactionTooLargeException e) {

                            // Ignore, keep going.

                        }

                    }

                }

            }

        }

        if (needOomAdj) {

            mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);

        }

    }

解决方案

后期cleanUpService是异步执行的不会有这个问题(即杀进程会在cleanUpServices之前,没有Service会被遍历到),当时让app在功能清单文件中配置android.R.attr#stopWithTask。

Service重启三千问

1. 什么情况下进程被杀后Service能重启?Start方式和bind方式的区别?

1. onStartCommand返回值START_STICKY_COMPATIBILITY/START_STICKY和START_REDELIVER_INTENT实现Service重启 有什么区别?

1. onStartCommand不同返回值有什么不同的效果?

1. 什么返回值Service重启后不执行onStartCommand?

1. 什么返回值Service重启不用重新发送intent?

1. 哪些情况下Service会被更快的重启?

1. START_REDELIVER_INTENT 重启时app的Service会执行几次onStartCommand?

1. start方式启动的Service的stopIfKilled为true时,Service一定不可以重启么?

1. deliveredStarts 什么时候添加?什么时候移除?

1. pendingStarts 什么时候添加?什么时候移除?

1. Service重启的restartDelay的计算逻辑?

1. 什么情况下START_REDELIVER_INTENT返回值不再执行重启?

参考文档:https://www.jianshu.com/p/7a659ca38510

你可能感兴趣的:(Service重启流程)