Service流程筆記

本文旨在記錄一下自己學習 Android Service 源碼時的筆記。

startService


首先看一下大致的流程圖


Context.startService

上圖中,AMS代表ActivityManagerService, AS代表ActiveServices。 這里假設Service的啟動者(ProcessA)和 Service(ProcessB)不在同一個進程, 當然 startService 方式更多情況下還是用在自己進程內部。 這里只列出一些我認為比較主要的函數,忽略了異常情況下的處理, 例如延遲start, 找不到對應進程等。 圖中顏色加深的幾個關鍵方法:

1) AS.retrieveServiceLocked: 從PackageManager查詢要啟動的Service信息
2)AMS.checkAllowBackgroundLocked: 檢查是否允許從后臺啟動Service
3)AS.bringUpServiceLocked: 對Service和Process的情況進行判度并處理,下面的那幾個判斷流程都是在該函數里
4)AMS.startProcessLocked: 啟動Service對應的進程
5)AS.realStartServiceLocked: 使用IApplicationThread接口向Service進程發送create消息
6)AS.sendServiceArgsLocked: 使用IApplicationThread接口向Service進程發送start消息
7)Service.onStartCommand & Service.onCreate: Service callback


下面開始代碼分析。
Context.startService
--> ContextImpl.startService
--> ContextImpl.startServiceCommon

前面經過Context和它的Wrapper模式下的調用棧, 進入ContextImpl.startServiceCommon

private ComponentName startServiceCommon(Intent service, UserHandle user) {
    try {
        //判斷Intent中是否符合要求, Android L以上版本需要顯示調用Service
        validateServiceIntent(service);

        //跨進程安全檢查
        service.prepareToLeaveProcess(this);

        //調用AMS接口
        ComponentName cn = ActivityManagerNative.getDefault().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), getOpPackageName(), user.getIdentifier());
       ......
        }
        return cn;
    } catch (RemoteException e) {
        ......
    }
}

startServiceCommon會先做一些檢查,其中 Intent.prepareToLeaveProcess對跨進程做安全性檢查,例如在N以后的版本,在StrictMode下,不能使用scheme為"file://"的URI進行數據傳輸,必須使用FileProvider代替

public void prepareToLeaveProcess(boolean leavingPackage) {
        ......
        switch (mAction) {
        ......
            default:
                // N以上版本對scheme為"file://" 的URI需要用FileProvider代替
                mData.checkFileUriExposed("Intent.getData()");
        }
}

然后調用服務端 AMS.startService,在此我們省去 Binder 調用流程,直接進入ActivityManagerService

public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, String callingPackage, int userId)
        throws TransactionTooLargeException {
    ......
    synchronized(this) {
        ......
        ComponentName res = mServices.startServiceLocked(caller, service,
                resolvedType, callingPid, callingUid, callingPackage, userId);
        ......
        return res;
    }
}

AMS.startService先對參數檢查, 然后調用ActiveServices.startServiceLocked

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

    // 1. 查詢待啟動Service的信息
    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg, false);
    ......

    // 2. 檢查是否可以在后臺啟動
    if (!r.startRequested) {
        try {
            final int allowed = mAm.checkAllowBackgroundLocked(
                    r.appInfo.uid, r.packageName, callingPid, true);
            if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                ......
                return null;
            } finally {
                ......
            }
        } 
    }
    ......

    r.startRequested = true;
    ......
    //startService可以多次向Service傳遞信息,每次的信息都是一個StartItem,對應著一個StartId
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
            service, neededGrants));

    // 3. 下面的部分用于判斷是否需要延遲啟動Service
    final ServiceMap smap = getServiceMap(r.userId);

    // addToStarting值決定是否將待啟動的Service加入后臺啟動隊列ServiceMap.mStartingBackground
    // 這里只是判斷, 它將被傳入最后的startServiceInnerLocked, 在那里使用
    // 另外一個隊列ServiceMap.mDelayedStartList則是延遲啟動隊列
    boolean addToStarting = false;
    if (!callerFg && r.app == null && mAm.mUserController.hasStartedUserState(r.userId)) {

        //通過AMS查詢Service對應的進程信息
        ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);

        //若Service對應的進程未啟動,或優先級過低,則需要將Service加入后臺啟動隊列
        if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
        ......
            //若當前用戶啟動的后臺服務數量已超上限,則延遲啟動服務
            if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
                smap.mDelayedStartList.add(r);
                r.delayed = true;
                return r.name;
            }

            addToStarting = true;
        } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
            ......
            addToStarting = true;
        }
    ......
    return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}

startServiceLocked 做了一些前期準備:
1. 查詢待啟動Service的信息
2. 檢查是否可以在后臺啟動
3. 檢查是否需要延遲啟動Service

這里第1步涉及到AMS對已啟動Service信息的存儲數據結構,我們在后面 AMS對Service的存儲 里一起分析.
第2步, O 上有一些改動, 具體可以看后面 Background Service in Android O 部分.
第3步將判斷待啟動的Service是否需要在 Background start, 或者是 Delay start, 總體規則就是如果進程未啟動或者優先級低,則 Background start, 如果連 Background start 隊列都已滿,則加入 Delay 隊列。 這一部分的看的不是很明白,特別是兩個隊列的關系, 我本以為是兩個優先級的隊列,但是看后面的處理函數ServiceMap.rescheduleDelayedStarts,好像不是, 以后有機會再看看。

檢查完成之后進入startServiceInnerLocked

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
        boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    ......
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);

    ......
    // 這里會根據前面傳進來的addToStarting值處理延遲Service
    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 里

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting, boolean permissionsReviewRequired)
        throws TransactionTooLargeException {
    ......
    // Case 1. 如果Service已經啟動過,直接發送start
    if (r.app != null && r.app.thread != null) {
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }
    ......
    // Case 2. Service沒啟動過 
    // 真正啟動之前再做一些檢查并處理, 例如service是否還有delay標志,service所處的進程時否結束了
    ......

    final String procName = r.processName;
    ProcessRecord app;
    if (!isolated) {
        // 查詢Service對應的進程信息
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (app != null && app.thread != null) {
            try {
                ......
                // Case 2.1 查詢到進程, 則直接start Service
                realStartServiceLocked(r, app, execInFg);
                return null;
            } 
            ......
        }
    } else {
       ......
    }
    
    // Case 2.2 查詢不到進程,則需要先start Process
    if (app == null && !permissionsReviewRequired) {
        //真正啟動進程
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                "service", r.name, false, isolated, false)) == null) {
            ......
            // Case 2.2.1 進程啟動失敗, stop service
            bringDownServiceLocked(r);
            return msg;
        }
        ......
    }
    // Case 2.2.2 進程啟動成功,把Service添加到mPendingServices中
    if (!mPendingServices.contains(r)) {
        mPendingServices.add(r);
    }

    return null;
}

bringUpServiceLocked 分幾種情況處理:
1 如果Service已經啟動過,調sendServiceArgsLocked發送start請求
2 如果Service還未啟動過, 查詢Serviced對應進程,看看進程是否已經啟動
2.1 進程已啟動, 調用realStartServiceLocked,創建Service
2.2 進程未啟動, 調用AMS.startProcessLocked啟動進程
2.2.1 進程啟動失敗, 調用bringDownServiceLocked結束Service
2.2.2 進程啟動成功, 將Service信息保存到mPendingServices,等待后面進程回調對其進行處理

這里我們簡單對2.2.2的情況說明一些,進程啟動成功后會調用AMS.attachApplicationLocked,在這里將mPendingServices里屬于該進程的Service啟動,調用的也是realStartServiceLocked

boolean attachApplicationLocked(ProcessRecord proc, String processName)
        throws RemoteException {
        ......
            for (int i=0; i<mPendingServices.size(); i++) {
                sr = mPendingServices.get(i);
                realStartServiceLocked(sr, proc, sr.createdFromFg);
            }
        ......
}

也就是說2.1和2.2.2最后都是進入realStartServiceLocked

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {

    ......
    try {
        ......
        // 發送create請求
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        ......
    } finally {
        ......
    }

    ......
    // 發送bind請求,對于startService,bind隊列并沒有增加
    // 所以在這種情況下requestServiceBindingsLocked相當于空函數
    // 后面分析bindService的時候我們再來看這個函數
    requestServiceBindingsLocked(r, execInFg);

    ......
    // 構造start參數StartItem
    if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                null, null));
    }
    // 發送start請求,同前面bringUpServiceLocked的Case 1
    sendServiceArgsLocked(r, execInFg, true);

    ......
}

realStartServiceLocked是通知客戶端創建和啟動Service的地方
1 調用ApplicationThead.scheduleCreateService通知客戶端創建服務
2 調用requestServiceBindingsLocked發送bind請求,使用于bindService情況
3 調用sendServiceArgsLocked發送start請求,使用于startService情況

這幾個步驟最后其實都差不多,都是AMS通過ApplicationThread對應的接口通知ActivityThread,以第一種情況為例,省去Binder調用流程,大致如下:
ApplicationThread.scheduleCreateService
--> ActivityThread.H.handleMessage(CREATE_SERVICE)
--> ActivityThread.handleCreateService

private void handleCreateService(CreateServiceData data) {
    ......
    Service service = null;
    try {
        // 使用反射創建 Service 實例
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
    } catch (Exception e) {
        ......
    }

    try {
        ......
        // 調用onCreate回調,然后保存起來
        service.onCreate();
        mServices.put(data.token, service);
        //通知AMS創建完成
        try {
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            
        }
        ......
    } catch (Exception e) {
        ......
    }
}

ActivityThread.handleCreateService創建Service實例,調用onCreate,并保存Service.

sendServiceArgsLocked流程差不多,最后由ActivityThread.handleServiceArgs處理,調用onStartCommon

private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
        boolean oomAdjusted) throws TransactionTooLargeException {
    ......
    while (r.pendingStarts.size() > 0) {
        ServiceRecord.StartItem si = null;
        try {
            ......
            si = r.pendingStarts.remove(0);
            // 記錄start次數
            si.deliveryCount++;

            int flags = 0;
            // 我們可以利用onStartCommand的flag參數來判斷是否是第一次start
            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);
        } 
        ......
    }
}

private void handleServiceArgs(ServiceArgsData data) {
    // 前面onCreate的時候已經保存了Service在mServices里,現在可以取出
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            ......
            if (!data.taskRemoved) {
                res = s.onStartCommand(data.args, data.flags, data.startId);
            } else {
                ......
            }
            ......
        } catch (Exception e) {
            ......
        }
    }
}

至此Context.startService完成

bindService


bindService流程同startService差不多,主要多了兩個點, 一個是AMS除了要保存Service信息,還要保存caller app和ServiceConnection信息(用于交互), 這個由ServiceRecord.retrieveAppBindingLocked負責; 另外就是bind完成要使用IActivityManager.publishService接口通知AMS,從而讓ServiceConnection.onServiceConnected執行

Context.bindService

下面開始看代碼
Context.bindService
--> ContextImpl.bindServiceCommon
--> AMS.bindService
--> AS.bindServiceLocked

前面的跟startService的差不多, 進入AS.bindServiceLocked

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, final IServiceConnection connection, int flags,
        String callingPackage, final int userId) throws TransactionTooLargeException {
    ......
    // 從ServiceMap查找ServiceRecord
    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
                Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
    ServiceRecord s = res.record;
    ......
    try {
        ......
        // 從ServiceRecord查找Client應用信息
        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
        // 構造ServiceConnection信息
        ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent);
        ......
        // 將ServiceConnection保存
        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);      
        
        clist = mServiceConnections.get(binder);
        if (clist == null) {
            clist = new ArrayList<ConnectionRecord>();
            mServiceConnections.put(binder, clist);
        }
        clist.add(c);

        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
            ......
            // bringUpService會通知客戶端onbind
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                    permissionsReviewRequired) != null) {
                return 0;
            }
            ......
        }
        ......
    } finally {
        ......
    }
}

bindServiceLocked流程比較簡單,先查找或者構造調用者app和ServiceConnection信息,存儲于ServiceRecord, 然后調用bringUpServiceLocked.

bringUpServiceLocked包含了startService和bindService的邏輯,startService的部分前面已經分析過, 這里看bindSerivce有關的流程:
bringUpServiceLocked
--> realStartServiceLocked
--> requestServiceBindingsLocked

// 對ServiceRecord.bindings里的元素依次調用requestServiceBindingLocked
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;
        }
    }
}

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;
}

requestServiceBindingsLocked則通過Binder最后進入ActivityThread.handleBindService:
ApplicationThread.scheduleBindService
--> ActivityThread.H.handleMessage(BIND_SERVICE)
--> ActivityThread.handleBindService

private void handleBindService(BindServiceData data) {
    // 前面Service onCreate的時候已被保存在mServices
    Service s = mServices.get(data.token);
    ......
    if (s != null) {
            ......
            try {
                if (!data.rebind) {
                    // 執行onBind回調
                    IBinder binder = s.onBind(data.intent);
                    // 通知AMS bind完成
                    ActivityManagerNative.getDefault().publishService(
                            data.token, data.intent, binder);
                } else {
                   ......
                }
               .
            } catch (RemoteException ex) {
                ......
            }
        } catch (Exception e) {
           ......
        }
}

handleBindService執行Service.onBind,并將結果通過AMS.publishService發送給AMS
AMS.publishService
--> AS.publishServiceLocked

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    ......
    try {
        if (r != null) {
            Intent.FilterComparison filter = new Intent.FilterComparison(intent);
            IntentBindRecord b = r.bindings.get(filter);
            if (b != null && !b.received) {
                // 記錄Service和狀態
                b.binder = service;
                b.requested = true;
                b.received = true;
                // 找出所有符合的ServiceConnection執行onServiceConnected
                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);
                        if (!filter.equals(c.binding.intent.intent)) {
                            ......
                            continue;
                        }
                       
                        try {
                            c.conn.connected(r.name, service);
                        } catch (Exception e) {
                            ......
                        }
                    }
                }
            }
            ......
        }
    } finally {
        ......
    }
}

publishService通知ServiceConnection執行onServiceConnected,至此bindService結束

AMS對Service的存儲(stopService/unbindService)


AMS里把所有啟動的Service存放在ActiveServices.mServiceMap, 這是一個SparseArray,可以看成一個Map,其key是調用者的userId,也就是說AMS將系統中所有啟動過的Service按調用者的userId分組. 為了方便分析,這里簡單的將userId等同于appId,即簡單的等同于一個應用.

而ServiceMap本身是存儲了兩個Map mServicesByNamemServicesByIntent, 分別代表顯式和隱式調用的Service信息

class ServiceMap extends Handler {
    final ArrayMap<ComponentName, ServiceRecord> mServicesByName = new ArrayMap<>();
    final ArrayMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent = new ArrayMap<>();
    ......
}

這里我們只取隱式的mServicesByIntent來分析,它的key是Intent.FilterComparison,可以把它看成是一個override了equals方法的Intent,我們選取最為常見的action作為Intent的代表,當然兩個Intent是否相等還要由 Data, Category 等比較決定,但是不影響我們對這里數據結構的分析. 所以最終我們發現,其實AMS用組合(caller appId, action)作為key來存儲已啟動的Service.

我們假設 Music 應用有一個 PlaybackService, 注冊的action有兩個.

<service android:name="com.haha.music.PlaybackService">
    <intent-filter>
        <action android:name="com.haha.music.action.PLAYBACK1" />
    </intent-filter>
    <intent-filter>
        <action android:name="com.haha.music.action.PLAYBACK2" />
    </intent-filter>
</service>

現在在Settings和Video兩個應用分別startService,其中Settings有兩處,用了兩個不同的action啟動

// In Settings process
startService(new Intent("com.haha.music.action.PLAYBACK1"));
...
startService(new Intent("com.haha.music.action.PLAYBACK2"));

// In Video process
startService(new Intent("com.haha.music.action.PLAYBACK1"));

最后內存里的情況如下:


了解了ServiceMap的結構之后,我們看startService/stopService時對Service的查找, 就是retrieveServiceLocked函數

private ServiceLookupResult retrieveServiceLocked(Intent service,
        String resolvedType, String callingPackage, int callingPid, int callingUid, int userId,
        boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal) {
    ServiceRecord r = null;
    // 得到當前用戶的userId
    userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
            ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);
    // 從ServiceMap中查找
    ServiceMap smap = getServiceMap(userId);
    final ComponentName comp = service.getComponent();
    if (comp != null) {
        r = smap.mServicesByName.get(comp);
    }
    if (r == null && !isBindExternal) {
        Intent.FilterComparison filter = new Intent.FilterComparison(service);
        r = smap.mServicesByIntent.get(filter);
    }

    // 如果Map里找不到則要開始構造這個ServiceRecord并保存起來
    if (r == null) {
        try {
            // PKMS根據參數得到對應Pkg中Serivce的ResolveInfo
            ResolveInfo rInfo = AppGlobals.getPackageManager().resolveService(service,
                    resolvedType, ActivityManagerService.STOCK_PM_FLAGS
                            | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
            ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null;

            ComponentName name = new ComponentName(
                    sInfo.applicationInfo.packageName, sInfo.name);
            
            r = smap.mServicesByName.get(name);
            if (r == null && createIfNeeded) {
                ......
                r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);
                smap.mServicesByName.put(name, r);
                smap.mServicesByIntent.put(filter, r);
                ......
            }
        } catch (RemoteException ex) {
            ......
        }
    }
    if (r != null) {
        return new ServiceLookupResult(r, null);
    }
    return null;
}

AS.retrieveServiceLocked其實就是從mServiceMap里查找對應的ServiceRecord,如果找不到就構造一個. 到此,這里的數據對startService/stopService已經夠用.

但是對bindService/unbindService, AMS還得記錄Client和ServiceConnection,這個由ServiceRecord.bindingsServiceRecord.connections 來負責. 通過簡化, 我們可以認為ServiceRecord.bindings用來存儲調用者應用信息, key為action, value為caller app信息(AppBinderRecord)隊列. ServiceRecord.connections存儲ServiceConnection, key為ServiceConnection在AMS的代表(binder). 這部分邏輯主要在retrieveAppBindingLocked和bindServiceLocked

public AppBindRecord retrieveAppBindingLocked(Intent intent,
        ProcessRecord app) {
    Intent.FilterComparison filter = new Intent.FilterComparison(intent);
    IntentBindRecord i = bindings.get(filter);
    if (i == null) {
        i = new IntentBindRecord(this, filter);
        bindings.put(filter, i);
    }
    AppBindRecord a = i.apps.get(app);
    if (a != null) {
        return a;
    }
    a = new AppBindRecord(this, i, app);
    i.apps.put(app, a);
    return a;
}

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, final IServiceConnection connection, int flags,
        String callingPackage, final int userId) throws TransactionTooLargeException {
    ......
    try {
        ......
        // 從ServiceRecord查找Client應用信息
        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
        // 構造ServiceConnection信息
        ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent);
        ......
        // 將ServiceConnection保存在ServiceRecord.connections
        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); 
     
        // 除了每個ServiceRecord保存著自己的connection隊列
        // ActiveServices里還有一個所有connection的隊列
        clist = mServiceConnections.get(binder);
        if (clist == null) {
            clist = new ArrayList<ConnectionRecord>();
            mServiceConnections.put(binder, clist);
        }
        clist.add(c);
        ......
    } finally {
        ......
    }
}

上面是屬于Service/ServiceConnection/Caller app的創建和保存,位于startService/bindService.

下面我們看看它們的刪除, 這個位于stopService和unbindService

private void stopServiceLocked(ServiceRecord service) {
    ......
    // 設置 startRequested 標志
    service.startRequested = false;
    ......
    // 注意這里的最后兩個參數值,都是false
    bringDownServiceIfNeededLocked(service, false, false);
}

// unbindService調用流程:
// AMS.unbindService --> AS.unbindServiceLocked --> AS.removeConnectionLocked
void removeConnectionLocked(
    ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
    ......
    AppBindRecord b = c.binding;

    ......
    // remove ServiceRecord.connectins里的記錄
    ArrayList<ConnectionRecord> clist = s.connections.get(binder);
    if (clist != null) {
        clist.remove(c);
        if (clist.size() == 0) {
            s.connections.remove(binder);
        }
    }

    ......
    // remove ActiveServices.mServiceConnections里的記錄
    clist = mServiceConnections.get(binder);
    if (clist != null) {
        clist.remove(c);
        if (clist.size() == 0) {
            mServiceConnections.remove(binder);
        }
    }

    if (!c.serviceDead) {
        ......
        // 調用onUnbind
        if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
                && b.intent.hasBound) {
            try {
                ......
                s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
            } catch (Exception e) {
                ......
            }
        }

        if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
            boolean hasAutoCreate = s.hasAutoCreateConnections();
            ......
            // 最后調用bringDownServiceIfNeededLocked, 注意這里最后兩個參數
            // 第一個為true, 第二個為s.hasAutoCreateConnections()
            bringDownServiceIfNeededLocked(s, true, hasAutoCreate);
        }
    }
}

stopService相對比較簡單,就是設置一下startRequested為false, 然后調用bringDownServiceIfNeededLocked, 注意這里傳入的最后兩個參數值,都是false

unbindService先找到要unbind的ServiceConnection, 調用removeConnectionLocked對其內部各種數據清理(主要是ServiceRecord.bindings, connections, ActiveServices.mServiceConnections), 然后使用ApplicationThread.scheduleUnbindService通知客戶端onUnbind, 最后也是調用bringDownServiceIfNeededLocked, 注意這里傳入的最后兩個參數值,但是其傳入的是true和s.hasAutoCreateConnections()

可以看到stop/unbind最終都會調用bringDownServiceIfNeededLocked

private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,
        boolean hasConn) {

    if (isServiceNeeded(r, knowConn, hasConn)) {
        return;
    }
    ......
    bringDownServiceLocked(r);
}

如果isServiceNeeded返回false就會執行bringDownServiceLocked,先看一下這個函數

private final void bringDownServiceLocked(ServiceRecord r) {
    ......

    final ServiceMap smap = getServiceMap(r.userId);
    smap.mServicesByName.remove(r.name);
    smap.mServicesByIntent.remove(r.intent);
    
    ......
    if (r.app != null) {
        ......
        if (r.app.thread != null) {
            ......
            try {
                ......
                r.app.thread.scheduleStopService(r);
            } catch (Exception e) {
                ......
            }
        } else {
            ......
        }
    } else {
        ......
    } 
    ......
}

bringDownServiceLocked其實就是清理ActiveServices.mServiceMap里的數據,并且通知Service.onDestory

回過頭看isServiceNeeded

private final boolean isServiceNeeded(ServiceRecord r, boolean knowConn, boolean hasConn) {
    // Are we still explicitly being asked to run?
    if (r.startRequested) {
        return true;
    }

    // Is someone still bound to us keepign us running?
    if (!knowConn) {
        hasConn = r.hasAutoCreateConnections();
    }
    if (hasConn) {
        return true;
    }

    return false;
}

//ServiceRecord.java
public boolean hasAutoCreateConnections() {
    for (int conni=connections.size()-1; conni>=0; conni--) {
        // unbindService --> removeConnectionLocked的時候會remove對應的connection
        // 所以剩下的還在connections中的就是還沒unbind的
        ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
        for (int i=0; i<cr.size(); i++) {
            if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
                return true;
            }
        }
    }
    return false;
}

結合上面stopService和unbindService調用時傳給它的參數值, 我們知道isServiceNeeded的邏輯等同于r.startRequested && r.hasAutoCreateConnections()

r.startRequested在startService時置為true, stopService置為false, r.hasAutoCreateConnections()則在沒有client bindService的時候返回false, 所以就是說bringDownServiceLocked,即Service.onDestory只有在調用過stopService(或者沒調用過startService)和所有connection都被unbind之后才會被執行

Background Service in Android O


現在Android對 Background Task(Service/Broadcast等)的限制已經越來越嚴格, 對Service而言,當進程處于后臺時, Android O 已經不允許 startService, 上面是N的代碼,我們簡單看下 O preview的流程

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, String callingPackage, final int userId)
        throws TransactionTooLargeException {
    ......
    if (!r.startRequested && !fgRequired) {
        final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
                r.appInfo.targetSdkVersion, callingPid, false, false);
    }
    ......
}

startServiceLocked 關于對于Service是否允許后臺start的判斷在O上有了變化,這里使用的是AMS.getAppStartModeLocked

int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
        int callingPid, boolean alwaysRestrict, boolean disabledOnly) {
    ......
    if (uidRec == null || alwaysRestrict || uidRec.idle) {
        ......
        if (ephemeral) {
            ......
        } else {
            ......
            //alwaysRestrict為false,所以調用appServicesRestrictedInBackgroundLocked
            final int startMode = (alwaysRestrict)
                    ? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
                    : appServicesRestrictedInBackgroundLocked(uid, packageName,
                            packageTargetSdk);
            
            //當Service的startMode不是Normal
            if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
                    ......
                    if (proc != null &&
                            //并且進程不是處于后臺
                            !ActivityManager.isProcStateBackground(proc.curProcState)) {
                        //那么就把startMode改為Normal
                        return ActivityManager.APP_START_MODE_NORMAL;
                    }
            }
            //否則返回原模式
            return startMode;
        }
    }
    ......
}

這里fgRequired參數在Context.startService時為false, 而Context.startForegroundService為true, 所以這里調用的是appRestrictedInBackgroundLocked

// 處于 Persistent/background whitelist/device idle white list 這三種情況下的 app 不受 background 限制 
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
    // Persistent app?
    if (mPackageManagerInt.isPackagePersistent(packageName)) {
        return ActivityManager.APP_START_MODE_NORMAL;
    }

    // Non-persistent but background whitelisted?
    if (uidOnBackgroundWhitelist(uid)) {
        return ActivityManager.APP_START_MODE_NORMAL;
    }

    // Is this app on the battery whitelist?
    if (isOnDeviceIdleWhitelistLocked(uid)) {
        return ActivityManager.APP_START_MODE_NORMAL;
    }

    // None of the service-policy criteria apply, so we apply the common criteria
    return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
}

int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
    // Apps that target O+ are always subject to background check
    if (packageTargetSdk >= Build.VERSION_CODES.O) {
        return ActivityManager.APP_START_MODE_DELAYED_RIGID;
    }
    ......
}

appRestrictedInBackgroundLocked對處于Persistent/background whitelist/device idle white list下的apps不設置Background limit,而其他的當targetSdkVersion >= O時, 則會限制

在Developer Guide的Background Execution Limits文章中, Google 建議是用 JobScheduler 來取代 Background task. 另外目前 Android O preview 版本還提供了 Context.startForegroundService, 按照其說法調用該接口創建Service后,需要在5秒(ANR interval)內調用 Service.startForeground(),否則 Service 就會被 stop 并且應用進程將 ANR.

目前 Android O 處于 preview 階段,不知道后續還會不會出現變化,因為看網上有一些文章提到 NotificationManager.startServiceInForeground(), 但是官網上已經找不到該接口了

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容