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

1、引言

Service做爲四大組件之一,在平時的開發中使用的頻率僅次於Activity。可是,咱們生成一個Service時,通常是不會重寫它的onStartCommand方法的。 究竟這個方法的返回值有什麼意義,Android系統爲何要給咱們提供這樣的一個方法呢?下面咱們仍是從源碼中尋找答案。bash

2、源碼分析

在以前【源碼解析】Service的工做過程 的文章中咱們分析過了Service的工做過程,知道Service的建立會走到ActiveService的realStartServiceLocked方法,咱們就從該方法入手:app

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方法,代碼以下:less

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方法,代碼以下:oop

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的代碼以下:post

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方法以下所示:ui

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方法,以下所示:this

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的值作了不一樣的處理:spa

  • START_STICKY_COMPATIBILITY 調用了ServiceRecord的findDeliveredStart方法將當前的Intent去掉,並將stopIfKilled置爲false。 查看START_STICKY_COMPATIBILITY註釋,說明以下:

其功能和START_STICKY同樣,是START_STICKY的兼容版本,可是不保證onStartCommand方法會被調用。3d

  • 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中的註釋對不上。後面有時間繼續研究,若是有小夥伴知道的話,也請賜教,萬分感謝!

3、總結

本文從源碼的角度並結合源碼註釋解析了Service的onStartCommand的返回值,分析了不一樣的返回值對Service的重建有不一樣的效果。 針對該結論,寫了一個小Demo進行驗證。文章傳送門:【Demo驗證】Service的onStartCommand返回值

相關文章
相關標籤/搜索