Service啓動流程

文中的源代碼版本爲api23java

Service啓動流程

啓動Service的方式有兩種:startServicebindServiceapi

startService

先祭上一張流程圖看個大概app

此圖省略了一些進程間通訊的細節,同時假設 Service進程已經啓動

Context.startService會經歷如下調用鏈 Context.startService-> ContextImpl.startService-> ContextImpl.startServiceCommonpost

ContextImpl.startServiceCommon

private ComponentName startServiceCommon(Intent service, UserHandle user) {
    try {
        //作一些校驗,如Android L版本及以上不容許隱式Intent啓動Service
        validateServiceIntent(service);
        service.prepareToLeaveProcess();
        
        //調用AMS的startService
        ComponentName cn = ActivityManagerNative.getDefault().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), getOpPackageName(), user.getIdentifier());
        if (cn != null) {
            //一些異常判斷
        }
        return cn;
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}
複製代碼

該方法最核心的做用是將啓動Service的請求經過IPC發送給了AMSui

AMS接收到請求以後會經歷如下方法調用 ActivityManagerService.startService-> ActiveService.startServiceLockedspa

ActiveService.startServiceLocked

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,//mimetype int callingPid, int callingUid, String callingPackage, int userId) throws TransactionTooLargeException {
    //...
    //記錄啓動Service的進程是否處於前臺
    final boolean callerFg;
    if (caller != null) {
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        if (callerApp == null) {
            //...
        }
        callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
    } else {
        callerFg = true;
    }


    //根據Intent信息查找ServiceLookupResult
    //ServiceLookupResult由ServiceRecord以及Service對應的permisson組成
    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg);
    //...
    r.lastActivity = SystemClock.uptimeMillis();
    r.startRequested = true;
    r.delayedStop = false;
    //保存請求數據,服務啓動以後會遍歷pendingStarts,取出StartItem
    //拿裏面的數據觸發Service.onStartCommand
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
            service, neededGrants));

    //ServiceMap與userId一一對應,保存服務信息
    final ServiceMap smap = getServiceMap(r.userId);
    //是否添加到服務啓動列表中
    //只有是後臺進程發起調用,且目標進程未啓動或者state>=PROCESS_STATE_SERVICE
    //才爲true
    boolean addToStarting = false;
    //延遲啓動的判斷
    if (!callerFg//調用方處於後臺,可能會延遲啓動Service
            && r.app == null //ServiceRecord未被分配進程
            && mAm.mStartedUsers.get(r.userId) != null) {
        //獲取應用進程實例
        ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
        if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
            //...
            if (r.delayed) {
                // This service is already scheduled for a delayed start; just leave
                // it still waiting.
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r);
                return r.name;
            }
            if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
                // Something else is starting, delay!
                Slog.i(TAG_SERVICE, "Delaying start of: " + r);
                smap.mDelayedStartList.add(r);
                r.delayed = true;
                return r.name;
            }
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
            addToStarting = true;
        } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
            addToStarting = true;
            //...
        } else if (DEBUG_DELAYED_STARTS) {
            //debug信息
        }
    } else if (DEBUG_DELAYED_STARTS) {
        //debug信息
    }

    return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);

複製代碼

ServiceMap這個結構用於保存用戶當前運行的服務信息,retrieveServiceLocked方法邏輯就是經過IntentServiceMap中查找Service,若是當前服務還未啓動,那麼會建立一個ServiceRecord對象,保存在ServiceMap中。 還有一個須要注意的點是後臺服務以及延遲啓動機制,全部的後臺服務都會被添加至一個名爲mStartingBackground的列表中(添加邏輯在startServiceInnerLocked方法中),若是當先後臺服務超過閾值,那麼服務將會被添加至延遲啓動列表中。debug

ActiveService.startServiceInnerLocked

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg,//服務啓動者是不是前臺進程 boolean addToStarting) //是否添加到後臺服務列表中 throws TransactionTooLargeException {
    //...
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);
    if (error != null) {
        return new ComponentName("!!", error);
    }
    
    //將服務添加至後臺服務列表中
    if (r.startRequested && addToStarting) {
        boolean first = smap.mStartingBackground.size() == 0;
        smap.mStartingBackground.add(r);
        r.startingBgTimeout = SystemClock.uptimeMillis() + BG_START_TIMEOUT;
        //...
        if (first) {
            smap.rescheduleDelayedStarts();
        }
    } else if (callerFg) {
        smap.ensureNotStartingBackground(r);
    }

    return r.name;
}
複製代碼

該方法主要是調用了bringUpServiceLocked來進行後續的服務啓動功能,同時會判斷addToStarting值決定是否將服務添加至後臺服務列表中。rest

ActiveService.bringUpServiceLocked

private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting) throws TransactionTooLargeException {

    //服務進程已經啓動,且服務也已啓動,分發onStartCommand事件
    if (r.app != null && r.app.thread != null) {
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }

    //...

    final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
    final String procName = r.processName;
    ProcessRecord app;

    //應用進程已經啓動,調用realStartServiceLocked,返回
    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);
        if (app != null && app.thread != null) {
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                realStartServiceLocked(r, app, execInFg);
                return null;
            } catch (TransactionTooLargeException e) {
                throw e;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting service " + r.shortName, e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }
    } else {
        app = r.isolatedProc;
    }
   
    //啓動服務進程
    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;
        }
    }

    //進程啓動須要時間,先將服務保存起來
    if (!mPendingServices.contains(r)) {
        mPendingServices.add(r);
    }

    //...

    return null;
}
複製代碼

這個方法根據服務進程的狀態以及服務的狀態產生了幾個分支code

  1. 若是服務進程已經啓動,且服務也已經啓動,那麼調用sendServiceArgsLocked方法觸發Service.onStartCommand
  2. 若是服務還未啓動,且不是以isotate模式啓動,那麼調用ActivityManagerService.getProcessRecordLocked查找是否有可用進程,若是有就調用realStartServiceLocked啓動服務
  3. 服務未啓動,且服務的進程也沒啓動,那麼調用ActivityManagerService.startProcessLocked啓動進程,同時將服務保存至待啓動列表中,等待進程啓動完畢以後繼續下面的流程。

sendServiceArgsLocked這個方法在realStartServiceLocked中也會被調用,咱們到時候再分析。cdn

關於進程啓動,你們能夠參考其餘文章,這裏不去展開講。 進程啓動以後,最終仍是會進入到realStartServiceLocked方法中,方法調用鏈以下 ActivityThread.main-> ActivityThread.attach-> ActivityManagerService.attachApplication-> ActivityManagerService.attachApplicationLocked-> ActiveService.attachApplicationLocked-> ActiveService.realStartServiceLocked

下面咱們就來分析一下ActiveService.realStartServiceLocked

ActiveService.realStartServiceLocked

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
    //...
    boolean created = false;
    try {
        //...
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        //...
    } finally {
        //...
    }

    //step2
    requestServiceBindingsLocked(r, execInFg);

    //...

    //step3
    sendServiceArgsLocked(r, execInFg, true);

    //...
}
複製代碼

這個方法我作了不少精簡,咱們只關注一些最核心的部分

  1. 跨進程調用ApplicationThrad.scheduleCreateService方法,最終會走到ActivityThread.handleCreateService中,該方法經過反射建立一個Service的實例,並調用其onCreate方法,這部分的代碼並不複雜,你們能夠自行分析。
  2. 調用requestServiceBindingsLocked處理bindService請求,咱們放到bindService啓動流程去講
  3. 調用sendServiceArgsLocked處理startService請求

下面咱們分析一下sendServiceArgsLocked

ActiveService.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);
            //...
            si.deliveredTime = SystemClock.uptimeMillis();
            r.deliveredStarts.add(si);
            si.deliveryCount++;
            //...
            r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
        } catch (TransactionTooLargeException e) {
           //...
        } catch (RemoteException e) {
            //...
        } catch (Exception e) {
            //...
        }

        //...
    }
}
複製代碼

還記得pendingStarts麼?在ActiveService.startServiceLocked方法中,ActiveService將啓動服務的請求的信息保存在pendingStarts中。 sendServiceArgsLocked的邏輯很是簡單,就是遍歷pendingStarts,將全部的start請求發送出去。 發送的過程有涉及到了進程間通訊,調用了ApplicationThrad.scheduleServiceArgs方法,該方法最終會進入到ActivityThread.handleServiceArgs中,觸發Service.onStartCommand

至此,startService的流程就結束了。

bindService

bindService的大體流程以下

圖中的流程是基於應用進程未啓動的場景進行繪製的

Context.bindService會經歷如下調用鏈 Context.bindService-> ContextImpl.bindService-> ContextImpl.bindServiceCommon

ContextImpl.bindServiceCommon

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, UserHandle user) {
    IServiceConnection sd;
    if (conn == null) {
        throw new IllegalArgumentException("connection is null");
    }
    if (mPackageInfo != null) {
        //bindService成功以後會回調給ServiceConnection一個binder對象
        //用於後續的C/S通訊,AMS直接經過IServiceConnection接口進行回調
        //因爲ServiceConnection不是aidl接口,所以須要進行必定的包裝
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                mMainThread.getHandler(), flags);
    } else {
        throw new RuntimeException("Not supported in system context");
    }
    validateServiceIntent(service);
    try {
        //...
        service.prepareToLeaveProcess();
        int res = ActivityManagerNative.getDefault().bindService(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, getOpPackageName(), user.getIdentifier());
        if (res < 0) {
            //...
        }
        return res != 0;
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}
複製代碼

bindServiceCommon的主要職責

  1. 調用LoadedApk.getServiceDispatcher是包裝ServiceConnection成一個IServiceConnection對象,後續AMS能夠直接經過該對象發佈客戶端與服務端進行通訊的Binder對象。這個方法就不展開講了。
  2. 跨進程調用ActivityManagerService.bindService方法繼續服務啓動流程

ActivityManagerService.bindService會直接調用ActiveService.bindServiceLocked

ActiveService.bindServiceLocked

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException {
    //bind請求發起進程
    final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
    //...

    //請求發起進程是否處於前臺
    final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;

    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage,
                Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
    //...
    ServiceRecord s = res.record;

    final long origId = Binder.clearCallingIdentity();

    try {
        //...

        //下面這些操做是在AMS中創建起客戶端和服務端的綁定關係
        //主要是保存客戶端的一些信息到ServiceRecord中
        //保存服務端的一些信息到客戶端(ActivityRecord、ProcessRecord)中
        
        //retrieveAppBindingLocked方法執行完畢以後會在ServiceRecord的bindings
        //字段中添加一條記錄。bindings是Map類型,值類型爲IntentBindRecord
        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
        ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent);

        IBinder binder = connection.asBinder();
        ArrayList<ConnectionRecord> clist = s.connections.get(binder);
        if (clist == null) {
            clist = new ArrayList<ConnectionRecord>();
            s.connections.put(binder, clist);
        }
        clist.add(c);
        b.connections.add(c);
        if (activity != null) {
            if (activity.connections == null) {
                activity.connections = new HashSet<ConnectionRecord>();
            }
            activity.connections.add(c);
        }
        b.client.connections.add(c);
        
        //...

        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
            s.lastActivity = SystemClock.uptimeMillis();
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
                return 0;
            }
        }

        //...

        if (s.app != null && b.intent.received) {
            // Service is already running, so we can immediately
            // publish the connection.
            try {
                //conn就是客戶端建立的IServiceConnection對象
                c.conn.connected(s.name, b.intent.binder);
            } catch (Exception e) {
                Slog.w(TAG, "Failure sending service " + s.shortName
                        + " to connection " + c.conn.asBinder()
                        + " (in " + c.binding.client.processName + ")", e);
            }

            //...
        } else if (!b.intent.requested) {
            requestServiceBindingLocked(s, b.intent, callerFg, false);
        }

        //...

    } finally {
        //...
    }

    return 1;
}
複製代碼

該方法的核心有兩點

  1. 調用bringUpServiceLocked啓動服務
  2. 調用IServiceConnection.connected方法發佈用於客戶端與服務端進行通訊的binder對象

看似簡單,可是若是將BIND_AUTO_CREATE標誌以及bringUpServiceLocked方法內部邏輯分支,場景會變得稍微複雜一些。這裏我對各個場景下Service.onBindServiceConnection.onServiceConnected的調用時機作一下簡單分析

設置BIND_AUTO_CREATE標誌,服務未啓動(包括進程未啓動)

因爲設置了BIND_AUTO_CREATE標誌,接下來就會進入到bringUpServiceLocked中。該方法在分析startService流程的時候咱們分析過。若是進程已經啓動則會進入到realStartServiceLocked中,若是進程未啓動,那麼待進程啓動以後經過進程間通訊最終仍是會走到realStartServiceLocked方法中。 realStartServiceLocked方法內會經過進程間通訊,通知服務進程建立Service實例,並調用onCreate方法,以後會調用requestServiceBindingsLocked方法處理bind請求,這個方法以前特地留到這裏進行分析。

ActiveService.requestServiceBindingsLocked
private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg) throws TransactionTooLargeException {
    for (int i=r.bindings.size()-1; i>=0; i--) {
        IntentBindRecord ibr = r.bindings.valueAt(i);
        if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
            break;
        }
    }
}
複製代碼

該方法很簡單,遍歷ServiceRecord.bindings的值,並挨個兒調用requestServiceBindingLocked

ActiveService.requestServiceBindingLocked
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException {
    //...
    if ((!i.requested || rebind) && i.apps.size() > 0) {
        try {
            //...
            r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                    r.app.repProcState);
            //...
        } catch (TransactionTooLargeException e) {
            //...
        } catch (RemoteException e) {
            //...
        }
    }
    return true;
}
複製代碼

requestServiceBindingLocked也很簡單,經過Binder通訊,調用ApplicationThread.scheduleBindService方法,該方法在通過一些列調用以後,Service.onBind就會被調用。

ServiceConnection.onServiceConnected的回調則是在Service.onBind方法被調用以後。 ActivityThread在調完Service.onBind以後,會回調AMS,最終進入到ActiveService.publishServiceLocked

ActiveService.publishServiceLocked
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    final long origId = Binder.clearCallingIdentity();
    try {
        //...
        if (r != null) {
            Intent.FilterComparison filter
                    = new Intent.FilterComparison(intent);
            IntentBindRecord b = r.bindings.get(filter);
            if (b != null && !b.received) {
                //...
                for (int conni=r.connections.size()-1; conni>=0; conni--) {
                    ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                    for (int i=0; i<clist.size(); i++) {
                        ConnectionRecord c = clist.get(i);
                        //...
                        try {
                            c.conn.connected(r.name, service);
                        } catch (Exception e) {
                            //...
                        }
                    }
                }
            }
            //...
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}
複製代碼

publishServiceLocked調用了IServiceConnection.connected,以後會通過進程間通訊以及IServiceConnection實現類的內部邏輯,最終調用ServiceConnection.onServiceConnected方法。

case 2 : 設置BIND_AUTO_CREATE標誌,服務已啓動

此時bringUpServiceLocked會立刻返回,繼續bindServiceLocked的流程

ActiveService.bindServiceLocked
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException {
    //...

    try {
        //...

        if (s.app != null && b.intent.received) {
            // Service is already running, so we can immediately
            // publish the connection.
            try {
                //conn就是客戶端建立的IServiceConnection對象
                c.conn.connected(s.name, b.intent.binder);
            } catch (Exception e) {
                Slog.w(TAG, "Failure sending service " + s.shortName
                        + " to connection " + c.conn.asBinder()
                        + " (in " + c.binding.client.processName + ")", e);
            }

            //...
        } else if (!b.intent.requested) {
            requestServiceBindingLocked(s, b.intent, callerFg, false);
        }

        //...

    } finally {
        //...
    }

    return 1;
}
複製代碼

這裏要注意的有兩個字段IntentBindRecord.requestedIntentBindRecord.received,它們會影響接下來的流程走向。

  • IntentBindRecord.requested 是否已經發起了Application.scheduleBindService調用,通知服務進程回調Service.onBind方法。
  • IntentBindRecord.received 該字段若是爲true,說明Service.onBind方法已經被回調

而此時因爲尚未調用ApplicationThread.scheduleBindService,所以receivedfalse,requested也是false,這時走requestServiceBindingLocked方法。 後續的流程,則跟case 1是同樣的。

case 3 : 未設置BIND_AUTO_CREATE,服務已啓動

若是未設置BIND_AUTO_CREATE,那麼在bind流程中是不會調用bringUpServiceLocked啓動服務的。而此時服務已經啓動,那麼多是以前有調用過startService啓動過服務。 此時receivedfalse,requested也是false,直接走requestServiceBindingLocked,跟case 2是同樣的

case 4 : 未設置BIND_AUTO_CREATE,服務未啓動

這種場景就須要等待服務啓動了,服務啓動的機制是下一次的startCommand被調用或者帶有BIND_AUTO_CREATE標誌的bindService被調用。不管進程是否啓動,最終都會走到realStartServiceLocked,後續流程case 1同樣。

至此,bindService的流程就分析完畢了。

相關文章
相關標籤/搜索