美文网首页
【源码解析】Service的onStartCommand返回值

【源码解析】Service的onStartCommand返回值

作者: AndroidHint | 来源:发表于2019-01-03 22:27 被阅读0次

    一、引言

    Service作为四大组件之一,在平时的开发中使用的频率仅次于Activity。但是,我们生成一个Service时,一般是不会重写它的onStartCommand方法的。
    究竟这个方法的返回值有什么意义,Android系统为什么要给我们提供这样的一个方法呢?下面我们还是从源码中寻找答案。

    二、源码分析

    在之前【源码解析】Service的工作过程 的文章中我们分析过了Service的工作过程,知道Service的创建会走到ActiveService的realStartServiceLocked方法,我们就从该方法入手:

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        ...省略
    
        boolean created = false;
        try {
            ...省略
            mAm.notifyPackageUse(r.serviceInfo.packageName,
                                 PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
            Slog.w(TAG, "Application dead when creating service " + r);
            mAm.appDiedLocked(app);
            throw e;
        } finally {
            ...省略
        }
    
        if (r.whitelistManager) {
            app.whitelistManager = true;
        }
    
        requestServiceBindingsLocked(r, execInFg);
    
        updateServiceClientActivitiesLocked(app, null, true);
    
        if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
            r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                    null, null, 0));
        }
    
        sendServiceArgsLocked(r, execInFg, true);
    
        ...省略
    }
    

    该方法通过app.thread.scheduleCreateService(app.thread是ActivityThread对象)创建了Service,然后调用了ActiveService的sendServiceArgsLocked方法,代码如下:

    private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
            boolean oomAdjusted) throws TransactionTooLargeException {
        final int N = r.pendingStarts.size();
        if (N == 0) {
            return;
        }
    
        ArrayList<ServiceStartArgs> args = new ArrayList<>();
    
        ...省略
    
        ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
        slice.setInlineCountLimit(4);
        Exception caughtException = null;
        try {
            r.app.thread.scheduleServiceArgs(r, slice);
        } catch (TransactionTooLargeException e) {
            ...省略
        } catch (RemoteException e) {
           ...省略
        } catch (Exception e) {
            ...省略
        }
        ...省略
    }
    

    sendServiceArgsLocked方法最终调用了r.app.thread.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);
        }
    }
    

    scheduleServiceArgs方法通过消息发送机制,发送一个标志位为H.SERVICE_ARGS的消息给到ActivityThread的内部类H处理,ActivityThread内部类H对于H.SERVICE_ARGS的处理逻辑是:

    case SERVICE_ARGS:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj)));
        handleServiceArgs((ServiceArgsData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    

    在handleMessage中对于SERVICE_ARGS的情况,调用了handleServiceArgs方法,handleServiceArgs的代码如下:

    private void handleServiceArgs(ServiceArgsData data) {
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                if (data.args != null) {
                    data.args.setExtrasClassLoader(s.getClassLoader());
                    data.args.prepareToEnterProcess();
                }
                int res;
                if (!data.taskRemoved) {
                    res = s.onStartCommand(data.args, data.flags, data.startId);
                } else {
                    s.onTaskRemoved(data.args);
                    res = Service.START_TASK_REMOVED_COMPLETE;
                }
    
                QueuedWork.waitToFinish();
    
                try {
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
                ensureJitEnabled();
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to start service " + s
                            + " with " + data.args + ": " + e.toString(), e);
                }
            }
        }
    }
    

    在上面的方法中显式的调用了Service的onStartCommand方法,得到一个整型返回值res,而我们本文需要分析的就是这个返回值。可以看到这个返回值res最终是作为了ActivityManager.getService().serviceDoneExecuting方法的参数,而ActivityManager.getService()就是ActivityManagerService对象,其中的serviceDoneExecuting方法如下所示:

    public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
        synchronized(this) {
            if (!(token instanceof ServiceRecord)) {
                Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
                throw new IllegalArgumentException("Invalid service token");
            }
            mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
        }
    }
    

    在serviceDoneExecuting方法中又调用了ActiveServices的serviceDoneExecutingLocked方法,如下所示:

    void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
        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.
                        r.findDeliveredStart(startId, true);
                        // Don't stop if killed.
                        r.stopIfKilled = false;
                        break;
                    }
                    case Service.START_NOT_STICKY: {
                        // We are done with the associated start arguments.
                        r.findDeliveredStart(startId, 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;
                    }
                    case Service.START_REDELIVER_INTENT: {
                        // We'll keep this item until they explicitly
                        // call stop for it, but keep track of the fact
                        // that it was delivered.
                        ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
                        if (si != null) {
                            si.deliveryCount = 0;
                            si.doneExecutingCount++;
                            // Don't stop if killed.
                            r.stopIfKilled = true;
                        }
                        break;
                    }
                    case Service.START_TASK_REMOVED_COMPLETE: {
                        // Special processing for onTaskRemoved().  Don't
                        // impact normal onStartCommand() processing.
                        r.findDeliveredStart(startId, true);
                        break;
                    }
                    default:
                        throw new IllegalArgumentException(
                                "Unknown service start result: " + res);
                }
                if (res == Service.START_STICKY_COMPATIBILITY) {
                    r.callStart = false;
                }
            } 
            ...省略
            final long origId = Binder.clearCallingIdentity();
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);
            Binder.restoreCallingIdentity(origId);
        } else {
            Slog.w(TAG, "Done executing unknown service from pid "
                    + Binder.getCallingPid());
        }
    }
    

    在serviceDoneExecutingLocked方法中,对于参数res的值做了不同的处理:

    • START_STICKY_COMPATIBILITY
      调用了ServiceRecord的findDeliveredStart方法将当前的Intent去掉,并将stopIfKilled置为false
      查看START_STICKY_COMPATIBILITY注释,说明如下:

    其功能和START_STICKY一样,是START_STICKY的兼容版本,但是不保证onStartCommand方法会被调用。

    • START_STICKY
      调用了ServiceRecord的findDeliveredStart方法将当前的Intent去掉,并将stopIfKilled置为false
      查看START_STICKY注释,说明如下:

    在Service启动后,如果该Service被系统杀死了,系统会重新创建该Service,onStartCommand方法会被调用,但是传给Service的intent为null。这种模式适合在用Service播放在后台播放音乐的场景。

    • START_NOT_STICKY
      调用了ServiceRecord的findDeliveredStart方法将当前的Intent去掉,并将stopIfKilled置为true
      查看START_NOT_STICKY注释,说明如下:

    在Service启动后,如果该Service被系统杀死了,系统不会重新创建该Service。除非开发者明确地使用startService的方式启动该Service。这种模式适用在使用Service存储一些数据的操作,在系统杀死Service时,操作停止也可以接受的场景。

    • START_REDELIVER_INTENT
      调用了ServiceRecord的findDeliveredStart方法,但不去掉当前Intent,并将stopIfKilled置为true
      查看START_REDELIVER_INTENT注释,说明如下:

    在Service启动后,如果该Service被系统杀死了,系统会重新创建该Service,onStartCommand方法会调用,并且将上次的Intent通过onStartCommand方法传递进来。除非开发者明确使用stopSelf方法停止该Service,否则其不会停止。

    在对参数res的值做了不同的处理后,调用了serviceDoneExecutingLocked方法,全局搜索了一下ServiceRecord的stopIfKilled值的引用,只有ActiveService的killServicesLocked方法对其进行了引用,代码如下:

    final void killServicesLocked(ProcessRecord app, boolean allowRestart) {
        ...省略
        for (int i=app.services.size()-1; i>=0; i--) {
            ServiceRecord sr = app.services.valueAt(i);
    
            // Unless the process is persistent, this process record is going away,
            // so make sure the service is cleaned out of it.
            if (!app.persistent) {
                app.services.removeAt(i);
            }
    
            // Sanity check: if the service listed for the app is not one
            // we actually are maintaining, just let it drop.
            final ServiceRecord curRec = smap.mServicesByName.get(sr.name);
            if (curRec != sr) {
                if (curRec != null) {
                    Slog.wtf(TAG, "Service " + sr + " in process " + app
                            + " not same as in map: " + curRec);
                }
                continue;
            }
    
            // Any services running in the application may need to be placed
            // back in the pending list.
            if (allowRestart && sr.crashCount >= mAm.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
                    && (sr.serviceInfo.applicationInfo.flags
                        &ApplicationInfo.FLAG_PERSISTENT) == 0) {
                Slog.w(TAG, "Service crashed " + sr.crashCount
                        + " times, stopping: " + sr);
                EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
                        sr.userId, sr.crashCount, sr.shortName, app.pid);
                bringDownServiceLocked(sr);
            } else if (!allowRestart
                    || !mAm.mUserController.isUserRunningLocked(sr.userId, 0)) {
                bringDownServiceLocked(sr);
            } else {
                boolean canceled = scheduleServiceRestartLocked(sr, true);
    
                // Should the service remain running?  Note that in the
                // extreme case of so many attempts to deliver a command
                // that it failed we also will stop it here.
                if (sr.startRequested && (sr.stopIfKilled || canceled)) {
                    if (sr.pendingStarts.size() == 0) {
                        sr.startRequested = false;
                        if (sr.tracker != null) {
                            sr.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
                                    SystemClock.uptimeMillis());
                        }
                        if (!sr.hasAutoCreateConnections()) {
                            // Whoops, no reason to restart!
                            bringDownServiceLocked(sr);
                        }
                    }
                }
            }
        }
    
        if (!allowRestart) {
            app.services.clear();
    
            // Make sure there are no more restarting services for this process.
            for (int i=mRestartingServices.size()-1; i>=0; i--) {
                ServiceRecord r = mRestartingServices.get(i);
                if (r.processName.equals(app.processName) &&
                        r.serviceInfo.applicationInfo.uid == app.info.uid) {
                    mRestartingServices.remove(i);
                    clearRestartingIfNeededLocked(r);
                }
            }
            for (int i=mPendingServices.size()-1; i>=0; i--) {
                ServiceRecord r = mPendingServices.get(i);
                if (r.processName.equals(app.processName) &&
                        r.serviceInfo.applicationInfo.uid == app.info.uid) {
                    mPendingServices.remove(i);
                }
            }
        }
    
        // Make sure we have no more records on the stopping list.
        int i = mDestroyingServices.size();
        while (i > 0) {
            i--;
            ServiceRecord sr = mDestroyingServices.get(i);
            if (sr.app == app) {
                sr.forceClearTracker();
                mDestroyingServices.remove(i);
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "killServices remove destroying " + sr);
            }
        }
    
        app.executingServices.clear();
    }
    

    killServicesLocked方法对于ServiceRecord的stopIfKilled的逻辑看不懂,和Service中的注释对不上。后面有时间继续研究,如果有小伙伴知道的话,也请赐教,万分感谢!

    三、总结

    本文从源码的角度并结合源码注释解析了Service的onStartCommand的返回值,分析了不同的返回值对Service的重建有不同的效果。
    针对该结论,写了一个小Demo进行验证。文章传送门:【Demo验证】Service的onStartCommand返回值

    相关文章

      网友评论

          本文标题:【源码解析】Service的onStartCommand返回值

          本文链接:https://www.haomeiwen.com/subject/qpgtrqtx.html