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

1、前言

最近在看關於插件化的知識,遇到了如何實現Service插件化的問題,所以,先學習一下Service內部的實現原理,這裏面會涉及到應用進程和ActivityManagerService的通訊,建議你們先閱讀一下以前的這篇文章 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通訊實現,整個Service的啓動過程離不開和AMS的通訊,從整個宏觀上來看,它的模型以下,是否是和咱們以前分析的通訊過程如出一轍: java

2、源碼分析

當咱們在Activity/Service/Application中,經過下面這個方法啓動Service時:緩存

public ComponentName startService(Intent service)
複製代碼

與以前分析過的不少源碼相似,最終會調用到ContextImpl的同名方法當中,而該方法又會調用內部的startServiceCommon(Intent, UserHandle)bash

在該方法中,主要作了兩件事:

  • 校驗Intent的合法性,對於Android 5.0之後,咱們要求Intent中須要制定PackageComponent中至少一個,不然會拋出異常。
  • 經過Binder通訊調用到ActivityManagerService的對應方法,關於調用的詳細過程能夠參考 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通訊實現 中對於應用進程到ActivityManagerService的通訊部分分析。

ActivityManagerService中的實現爲: 數據結構

這裏面,經過 mServices變量來去實現 Service的啓動,而 mServices的類型爲 ActiveServices,它是 ActivityManagerService中負責處理 Service相關業務的類,不管咱們是經過 start仍是 bind方式啓動的 Service,都是經過它來實現的,它也被稱爲「應用服務」的管理類。

從經過ActiveServices調用startServiceLocked,到Service真正被啓動的過程以下圖所示,下面,咱們就來一一分析每個階段究竟作了什麼: app

咱們首先來看入口函數startServiceLocked的內部實現:ide

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及其所屬進程的狀態,走向不一樣的分支:post

  • 進程已經存在,而且目標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這條消息:

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的註釋,也能夠查看網上的一些教程:

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中的下面這個消息處理分支當中:

具體的 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方法,初始化成員變量。
  • 調用ServiceonCreate()方法。
  • 將該Service經過token做爲key,緩存在mServices列表當中,以後Service生命週期的回調,都依賴於該列表。

2.3 進程不存在的狀況

在這種狀況下,Service天然也不會存在,咱們會走到mAm.startProcessLocked的邏輯,這裏會去啓動Service所在的進程,它到底是怎麼啓動的咱們先不去細看,只須要知道它是這個目的就能夠了,此外,還須要注意的是它將該ServiceRecord加入到了mPendingServices中。

對於Service所在的進程,它的入口函數爲ActivityThreadmain()方法,在main方法中,會建立一個ApplicationThread對象,並調用它的attach方法:

而在 attach方法中,會調用到 AMSattachApplication方法:
attachApplication方法中,又會去調用內部的 attachApplicationLocked
這裏面的邏輯比較長,咱們只須要關注下面這句,咱們又看到了熟悉的 mServices對象:
ActiveServices中的該方法中,就會去遍歷前面談到的 mPendingServices列表,再依次調用 realStartServiceLocked方法,至於這個方法作了什麼,你們能夠回到前面的 2.2節去看,這裏就再也不重複分析了。

3、小結

以上就是startService的整個流程,bindService也是相似一個調用過程,其過程並不複雜,本質上仍是 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通訊實現 所談到的通訊過程,咱們所須要學習的是AMS端和Service所在的應用進程對於Service是如何管理的。

系統當中的全部Service都是經過AMSmServices變量,也就是ActiveServices類來進行管理的,而且每個應用進程中的Service都會在AMS端會對應一個ServiceRecord對象,ServiceRecord中維護了應用進程中的Service對象所須要的狀態信息。

而且,不管咱們調用多少次startService方法,在應用進程側都會只存在一個Service的實例,它被存儲到ActivityThreadArrayMap類型的mServices變量當中。


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

相關文章
相關標籤/搜索