Framework 原始碼解析知識梳理(5) startService 原始碼分析

澤毛發表於2017-12-21

一、前言

最近在看關於外掛化的知識,遇到了如何實現Service外掛化的問題,因此,先學習一下Service內部的實現原理,這裡面會涉及到應用程式和ActivityManagerService的通訊,建議大家先閱讀一下之前的這篇文章 Framework 原始碼解析知識梳理(1) - 應用程式與 AMS 的通訊實現,整個Service的啟動過程離不開和AMS的通訊,從整個巨集觀上來看,它的模型如下,是不是和我們之前分析的通訊過程一模一樣:

Framework 原始碼解析知識梳理(5)   startService 原始碼分析

二、原始碼分析

當我們在Activity/Service/Application中,通過下面這個方法啟動Service時:

public ComponentName startService(Intent service)
複製程式碼

與之前分析過的很多原始碼類似,最終會呼叫到ContextImpl的同名方法當中,而該方法又會呼叫內部的startServiceCommon(Intent, UserHandle)

Framework 原始碼解析知識梳理(5)   startService 原始碼分析
在該方法中,主要做了兩件事:

  • 校驗Intent的合法性,對於Android 5.0以後,我們要求Intent中需要制定PackageComponent中至少一個,否則會丟擲異常。
  • 通過Binder通訊呼叫到ActivityManagerService的對應方法,關於呼叫的詳細過程可以參考 Framework 原始碼解析知識梳理(1) - 應用程式與 AMS 的通訊實現 中對於應用程式到ActivityManagerService的通訊部分分析。

ActivityManagerService中的實現為:

Framework 原始碼解析知識梳理(5)   startService 原始碼分析
這裡面,通過mServices變數來去實現Service的啟動,而mServices的型別為ActiveServices,它是ActivityManagerService中負責處理Service相關業務的類,無論我們是通過start還是bind方式啟動的Service,都是通過它來實現的,它也被稱為“應用服務”的管理類。

從通過ActiveServices呼叫startServiceLocked,到Service真正被啟動的過程如下圖所示,下面,我們就來一一分析每一個階段究竟做了什麼:

Framework 原始碼解析知識梳理(5)   startService 原始碼分析

我們首先來看入口函式startServiceLocked的內部實現:

    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, String callingPackage, final int userId)
            throws TransactionTooLargeException {

        final boolean callerFg;
        //caller是呼叫者程式在AMS的遠端代理物件,型別為ApplicationThreadProxy。
        if (caller != null) {
            //獲得呼叫者的程式資訊。
            final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
            if (callerApp == null) {
                throw new SecurityException(
                        "Unable to find app for caller " + caller
                        + " (pid=" + Binder.getCallingPid()
                        + ") when starting service " + service);
            }
            callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
        } else {
            callerFg = true;
        }

        //通過傳遞的資訊,解析出要啟動的Service,封裝在ServiceLookupResult中。
        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true, callerFg, false);
        if (res == null) {
            return null;
        }
        if (res.record == null) {
            return new ComponentName("!", res.permission != null
                    ? res.permission : "private to package");
        }
        //對於每個被啟動的Service,它們在AMS端的資料結構為ServiceRecord。
        ServiceRecord r = res.record;

        if (!mAm.mUserController.exists(r.userId)) {
            Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
            return null;
        }
        //判斷是否允許啟動該服務。
        if (!r.startRequested) {
            final long token = Binder.clearCallingIdentity();
            try {
                final int allowed = mAm.checkAllowBackgroundLocked(
                        r.appInfo.uid, r.packageName, callingPid, true);
                if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                    Slog.w(TAG, "Background start not allowed: service "
                            + service + " to " + r.name.flattenToShortString()
                            + " from pid=" + callingPid + " uid=" + callingUid
                            + " pkg=" + callingPackage);
                    return null;
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
        //是否需要配置相應的許可權。
        NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
                callingUid, r.packageName, service, service.getFlags(), null, r.userId);

        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
            if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
                    callingUid, service, callerFg, userId)) {
                return null;
            }
        }
        //如果該Service正在等待被重新啟動,那麼移除它。
        if (unscheduleServiceRestartLocked(r, callingUid, false)) {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
        }
        //給ServiceRecord新增必要的資訊。
        r.lastActivity = SystemClock.uptimeMillis();
        r.startRequested = true;
        r.delayedStop = false;
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                service, neededGrants));
        //其它的一些邏輯,與第一次啟動無關,就先不分析了。
        return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    }
複製程式碼

簡單地來說,就是根據Intent查詢需要啟動的Service,封裝成ServiceRecord物件,初始化其中的關鍵變數,在這一過程中,加入了一些必要的邏輯判斷,最終呼叫了startServiceInnerLocked方法。

    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
        //...
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
        //...
        return r.name;
    }
複製程式碼

這裡面的邏輯我們先忽略掉一部分,只需要知道在正常情況下它會去呼叫bringUpServiceLocked來啟動Service

    private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting) throws TransactionTooLargeException {
 
        //如果該Service已經啟動。
        if (r.app != null && r.app.thread != null) {
            sendServiceArgsLocked(r, execInFg, false);
            return null;
        }
        //如果正在等待被重新啟動,那麼什麼也不做。
        if (!whileRestarting && r.restartDelay > 0) {
            return null;
        }

        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent);

        //清除等待被重新啟動的狀態。
        if (mRestartingServices.remove(r)) {
            r.resetRestartCounter();
            clearRestartingIfNeededLocked(r);
        }

        //因為我們馬上就要啟動該Service,因此去掉它的延時屬性。
        if (r.delayed) {
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);
            getServiceMap(r.userId).mDelayedStartList.remove(r);
            r.delayed = false;
        }

        //如果該Service所屬的使用者沒有啟動,那麼呼叫 bringDownServiceLocked 方法。
        if (mAm.mStartedUsers.get(r.userId) == null) {
            String msg = "Unable to launch app "
                    + r.appInfo.packageName + "/"
                    + r.appInfo.uid + " for service "
                    + r.intent.getIntent() + ": user " + r.userId + " is stopped";
            Slog.w(TAG, msg);
            bringDownServiceLocked(r);
            return msg;
        }

        try {
            AppGlobals.getPackageManager().setPackageStoppedState(
                    r.packageName, false, r.userId);
        } catch (RemoteException e) {
        } catch (IllegalArgumentException e) {
            Slog.w(TAG, "Failed trying to unstop package "
                    + r.packageName + ": " + e);
        }

        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
        final String procName = r.processName;
        ProcessRecord app;
        //如果不是執行在獨立的程式。
        if (!isolated) {
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                        + " app=" + app);
            //如果該程式已經啟動,那麼呼叫realStartServiceLocked方法。
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    //在Service所屬程式已經啟動的情況下呼叫的方法。
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
                }
            }
        } else {
            app = r.isolatedProc;
        }

        //如果該Service所對應的程式沒有啟動,那麼首先啟動該程式。
        if (app == null) {
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated, false)) == null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                Slog.w(TAG, msg);
                bringDownServiceLocked(r);
                return msg;
            }
            if (isolated) {
                r.isolatedProc = app;
            }
        }
        //將該ServiceRecord加入到等待的集合當中,等到新的程式啟動之後,再去啟動它。
        if (!mPendingServices.contains(r)) {
            mPendingServices.add(r);
        }

        if (r.delayedStop) {
            r.delayedStop = false;
            if (r.startRequested) {
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                        "Applying delayed stop (in bring up): " + r);
                stopServiceLocked(r);
            }
        }

        return null;
    }
複製程式碼

bringUpServiceLocked的邏輯就比較複雜了,它會根據目標Service及其所屬程式的狀態,走向不同的分支:

  • 程式已經存在,並且目標Service已經啟動:sendServiceArgsLocked
  • 程式已經存在,但是目標Service沒有啟動:realStartServiceLocked
  • 程式不存在:startProcessLocked,並且將ServiceRecord加入到mPendingServices中,等待程式啟動之後再去啟動該Service

下面,我們就來一起分析一下這三種情況。

2.1 程式已經存在,並且目標 Service 已經啟動

首先來當程式已經存在,且目標Service已經啟動時所呼叫的sendServiceArgsLocked方法:

    private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
            boolean oomAdjusted) throws TransactionTooLargeException {
        final int N = r.pendingStarts.size();
        if (N == 0) {
            return;
        }

        while (r.pendingStarts.size() > 0) {
            Exception caughtException = null;
            ServiceRecord.StartItem si;
            try {
                si = r.pendingStarts.remove(0);
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Sending arguments to: "
                        + r + " " + r.intent + " args=" + si.intent);
                if (si.intent == null && N > 1) {
                    // If somehow we got a dummy null intent in the middle,
                    // then skip it.  DO NOT skip a null intent when it is
                    // the only one in the list -- this is to support the
                    // onStartCommand(null) case.
                    continue;
                }
                si.deliveredTime = SystemClock.uptimeMillis();
                r.deliveredStarts.add(si);
                si.deliveryCount++;
                if (si.neededGrants != null) {
                    mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
                            si.getUriPermissionsLocked());
                }
                bumpServiceExecutingLocked(r, execInFg, "start");
                if (!oomAdjusted) {
                    oomAdjusted = true;
                    mAm.updateOomAdjLocked(r.app);
                }
                int flags = 0;
                if (si.deliveryCount > 1) {
                    flags |= Service.START_FLAG_RETRY;
                }
                if (si.doneExecutingCount > 0) {
                    flags |= Service.START_FLAG_REDELIVERY;
                }
                r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
            } catch (TransactionTooLargeException e) {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large: intent="
                        + si.intent);
                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);
                caughtException = e;
            } catch (Exception e) {
                Slog.w(TAG, "Unexpected exception", e);
                caughtException = e;
            }

            if (caughtException != null) {
                // Keep nesting count correct
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                if (caughtException instanceof TransactionTooLargeException) {
                    throw (TransactionTooLargeException)caughtException;
                }
                break;
            }
        }
    }
複製程式碼

這裡面最關鍵的就是呼叫了r.app.threadscheduleServiceArgs方法,它其實就是Service所屬程式的ApplicationThread物件在AMS端的一個遠端代理物件,也就是ApplicationThreadProxy,通過binder通訊,它最終會回撥到Service所屬程式的ApplicationThreadscheduleServiceArgs中:

    private class ApplicationThread extends ApplicationThreadNative {

        public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
            int flags ,Intent args) {
            ServiceArgsData s = new ServiceArgsData();
            s.token = token;
            s.taskRemoved = taskRemoved;
            s.startId = startId;
            s.flags = flags;
            s.args = args;
            sendMessage(H.SERVICE_ARGS, s);
        }
   }
複製程式碼

sendMessage函式會通過ActivityThread內部的mH物件傳送訊息到主執行緒當中,mH實際上是一個Handler的子類,在它的handleMessage回撥中,處理H.SERVICE_ARGS這條訊息:

Framework 原始碼解析知識梳理(5)   startService 原始碼分析
handleServiceArgs方法,就會從mServices去查詢對應的Service,呼叫我們熟悉的onStartCommand方法,至於Service物件是如何被加入到mServices中的,我們在2.2節中進行分析。

    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) {
                    //呼叫 onStartCommand 方法。
                    res = s.onStartCommand(data.args, data.flags, data.startId);
                } else {
                    s.onTaskRemoved(data.args);
                    res = Service.START_TASK_REMOVED_COMPLETE;
                }

                QueuedWork.waitToFinish();

                try {
                    //通知AMS啟動完成。
                    ActivityManagerNative.getDefault().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
                } catch (RemoteException e) {
                    // nothing to do.
                }
                ensureJitEnabled();
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to start service " + s
                            + " with " + data.args + ": " + e.toString(), e);
                }
            }
        }
    }
複製程式碼

這裡面,我們會取得onStartCommand的返回值,再通過Binder將該返回值傳回到AMS端,在AMSmServicesserviceDoneExecutingLocked中,會根據該返回值來修改ServiceRecord的屬性,這也就是我們常說的onStartCommand方法的返回值,會影響到該Services之後的一些行為的原因,關於這些返回值之間的差別,可以檢視Service.java的註釋,也可以檢視網上的一些教程:

Framework 原始碼解析知識梳理(5)   startService 原始碼分析

2.2 程式已經存在,但是 Service 沒有啟動

下面,我們來看Service沒有啟動的情況,這裡會呼叫realStartServiceLocked方法:

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        if (app.thread == null) {
            throw new RemoteException();
        }
        if (DEBUG_MU)
            Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
                    + ", ProcessRecord.uid = " + app.uid);
        r.app = app;
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();

        final boolean newService = app.services.add(r);
        bumpServiceExecutingLocked(r, execInFg, "create");
        //更新Service所在程式的oom_adj值。
        mAm.updateLruProcessLocked(app, false, null);
        mAm.updateOomAdjLocked();

        boolean created = false;
        try {
            if (LOG_SERVICE_START_STOP) {
                String nameTerm;
                int lastPeriod = r.shortName.lastIndexOf('.');
                nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
                EventLogTags.writeAmCreateService(
                        r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
            }
            synchronized (r.stats.getBatteryStats()) {
                r.stats.startLaunchedLocked();
            }
            mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            //通知應用端建立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 (!created) {
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                if (newService) {
                    app.services.remove(r);
                    r.app = null;
                }
                if (!inDestroying) {
                    scheduleServiceRestartLocked(r, false);
                }
            }
        }
        //這裡面會遍歷 ServiceRecord.bindings 列表,因為我們這裡是用startService方式啟動的,因此該列表為空,什麼也不會呼叫。
        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));
        }
        //這個在第一種情況中分析過了,會呼叫 onStartCommand 方法。
        sendServiceArgsLocked(r, execInFg, true);

        if (r.delayed) {
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);
            getServiceMap(r.userId).mDelayedStartList.remove(r);
            r.delayed = false;
        }

        if (r.delayedStop) {
            // Oh and hey we've already been asked to stop!
            r.delayedStop = false;
            if (r.startRequested) {
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                        "Applying delayed stop (from start): " + r);
                stopServiceLocked(r);
            }
        }
    }
複製程式碼

這段邏輯,有三個地方需要注意:

  • app.thread.scheduleCreateService:通過這裡會在應用程式建立Service物件
  • requestServiceBindingsLocked:如果是通過bindService方式啟動的,那麼會去呼叫它的onBind方法。
  • sendServiceArgsLocked:正如2.1節中所分析,這裡最終會觸發應用程式的ServiceonStartCommand方法被呼叫。

第二點由於我們今天分析的是startService的方式,所以不用在意,而第三點我們之前已經分析過了。所以我們主要關注app.thread.scheduleCreateService這一句,和2.1中的過程類似,它會回撥到H中的下面這個訊息處理分支當中:

Framework 原始碼解析知識梳理(5)   startService 原始碼分析
具體的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.
        unscheduleGcIdler();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            //根據Service的名字,動態的載入該Service類。
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to instantiate service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }

        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
            //1.建立ContextImpl物件。
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            //2.將 Service 作為它的成員變數 mOuterContext 。
            context.setOuterContext(service);
            //3.建立Application物件,如果已經建立那麼直接返回,否則先呼叫Application的onCreate方法。
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            //4.呼叫attach方法,將ContextImpl作為它的成員變數mBase。
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            //5.呼叫Service的onCreate方法。
            service.onCreate();
            //6.將該Service物件快取起來。
            mServices.put(data.token, service);
            try {
                ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                // nothing to do.
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to create service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }
    }
複製程式碼

以上的邏輯分為以下幾個部分:

  • 根據Service的名字,通過ClassLoader載入該類,並生成一個Service例項。
  • 建立一個新的ContextImpl物件,並將第一步中建立的Service例項作為它的mOuterContext變數。
  • 建立Application物件,這裡會先判斷當前程式所對應的Application物件是否已經建立,如果已經建立,那麼就直接返回,否則會先建立它,並依次呼叫它的attachBaseContext(Context context)onCreate()方法。
  • 呼叫Serviceattach方法,初始化成員變數。
    Framework 原始碼解析知識梳理(5)   startService 原始碼分析
  • 呼叫ServiceonCreate()方法。
  • 將該Service通過token作為key,快取在mServices列表當中,之後Service生命週期的回撥,都依賴於該列表。

2.3 程式不存在的情況

在這種情況下,Service自然也不會存在,我們會走到mAm.startProcessLocked的邏輯,這裡會去啟動Service所在的程式,它究竟是怎麼啟動的我們先不去細看,只需要知道它是這個目的就可以了,此外,還需要注意的是它將該ServiceRecord加入到了mPendingServices中。

對於Service所在的程式,它的入口函式為ActivityThreadmain()方法,在main方法中,會建立一個ApplicationThread物件,並呼叫它的attach方法:

Framework 原始碼解析知識梳理(5)   startService 原始碼分析
而在attach方法中,會呼叫到AMSattachApplication方法:
Framework 原始碼解析知識梳理(5)   startService 原始碼分析
attachApplication方法中,又會去呼叫內部的attachApplicationLocked
Framework 原始碼解析知識梳理(5)   startService 原始碼分析
這裡面的邏輯比較長,我們只需要關注下面這句,我們又看到了熟悉的mServices物件:
Framework 原始碼解析知識梳理(5)   startService 原始碼分析
ActiveServices中的該方法中,就會去遍歷前面談到的mPendingServices列表,再依次呼叫realStartServiceLocked方法,至於這個方法做了什麼,大家可以回到前面的2.2節去看,這裡就不再重複分析了。
Framework 原始碼解析知識梳理(5)   startService 原始碼分析

三、小結

以上就是startService的整個流程,bindService也是類似一個呼叫過程,其過程並不複雜,本質上還是 Framework 原始碼解析知識梳理(1) - 應用程式與 AMS 的通訊實現 所談到的通訊過程,我們所需要學習的是AMS端和Service所在的應用程式對於Service是如何管理的。

系統當中的所有Service都是通過AMSmServices變數,也就是ActiveServices類來進行管理的,並且每一個應用程式中的Service都會在AMS端會對應一個ServiceRecord物件,ServiceRecord中維護了應用程式中的Service物件所需要的狀態資訊。

並且,無論我們呼叫多少次startService方法,在應用程式側都會只存在一個Service的例項,它被儲存到ActivityThreadArrayMap型別的mServices變數當中。


更多文章,歡迎訪問我的 Android 知識梳理系列:

相關文章