bindService啟動源碼分析

上一篇
startService啟動源碼分析

1.Service生命周期

上篇講到了startService的生命周期特姐,這篇的話,講第二種Service的啟動方式呆盖,bindService呢灶。

2.BindService時(shí)序圖

老規(guī)矩曾雕,看代碼前先放圖和措。相較startService庄呈,bindService的過程稍微復(fù)雜些。這里先介紹一下新增的幾個類派阱。
ServiceDispatcher 應(yīng)用端的Service連接的管理類诬留。管理連接的binder的狀態(tài)。
InnerConnection IServiceConnection.Stub的子類贫母。連接狀態(tài)控制Client和Service傳輸?shù)腷inder媒介文兑。
ActiveServices system_server端Service行為的管理類。
ServiceConnection 重寫它的兩個回調(diào)方法onServiceConnected()和onServiceDisconnected()腺劣,能在Service綁定和解綁時(shí)得到回調(diào)绿贞。

BindService時(shí)序圖

3.源碼分析

虛擬框架公司在國內(nèi)搭了一個AOSP源碼查詢的網(wǎng)站,更新的代碼已經(jīng)到Android Q橘原。所以之后的話可以直接看Q的代碼籍铁。
和startService類似,bindService事實(shí)上通過mBase調(diào)用到ContextImplbindService趾断。一路調(diào)用到ActivityManager.getService().startService拒名。前篇已經(jīng)說過,這個通過IPC調(diào)用到System_service進(jìn)程的ActivityManagerService芋酌。
ContextImpl


    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());
    }

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (mPackageInfo != null) {
            //這里通過將傳入的ServiceConnection進(jìn)行緩存封裝增显,得到一個IServiceConnection對象。
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                flags |= BIND_WAIVE_PRIORITY;
            }
            service.prepareToLeaveProcess(this);
            int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

這里先看一下getServiceDispatcher的操作脐帝。這里以context為key值同云,保存了連接類ServiceConnection和傳輸類IServiceConnection的值。
LoadedApk

    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            if (map != null) {
                if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);
                sd = map.get(c);
            }
            if (sd == null) {
                sd = new ServiceDispatcher(c, context, handler, flags);
                if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
                if (map == null) {
                    map = new ArrayMap<>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler);
            }
            return sd.getIServiceConnection();
        }
    }

ActivityManagerService還是直接調(diào)用ActiveServicesbindServiceLocked方法腮恩。
ActivityManagerService

    public int bindService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags,
            String callingPackage, int userId) throws TransactionTooLargeException {
        return bindIsolatedService(caller, token, service, resolvedType, connection, flags,
                null, callingPackage, userId);
    }

    public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String instanceName,
            String callingPackage, int userId) throws TransactionTooLargeException {
        enforceNotIsolatedCaller("bindService");

        // Refuse possible leaked file descriptors
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        // Ensure that instanceName, which is caller provided, does not contain
        // unusual characters.
        if (instanceName != null) {
            for (int i = 0; i < instanceName.length(); ++i) {
                char c = instanceName.charAt(i);
                if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
                            || (c >= '0' && c <= '9') || c == '_' || c == '.')) {
                    throw new IllegalArgumentException("Illegal instanceName");
                }
            }
        }

        synchronized(this) {
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, instanceName, callingPackage, userId);
        }
    }

    public void updateServiceGroup(IServiceConnection connection, int group, int importance) {
        synchronized (this) {
            mServices.updateServiceGroupLocked(connection, group, importance);
        }
    }

bindServiceLocked的過程梢杭,創(chuàng)建了一個bindings不為空的ServiceRecord對象,往下傳遞秸滴。我們啟動Service的時(shí)候武契,需要傳遞標(biāo)志位Context.BIND_AUTO_CREATE。所以這里可以看到荡含,如果Service不在運(yùn)行狀態(tài)咒唆,又跑入了上一篇描述的bringUpServiceLocked流程。這里不再此描述此流程释液∪停可以回看上一篇。
ActiveServices

        int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,

        ...
        try {
            ...
            mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
                    callerApp.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
                    s.instanceName, s.processName);
            // Once the apps have become associated, if one of them is caller is ephemeral
            // the target app should now be able to see the calling app
            mAm.grantEphemeralAccessLocked(callerApp.userId, service,
                    UserHandle.getAppId(s.appInfo.uid), UserHandle.getAppId(callerApp.uid));

            //這里對ServiceRecord的bindings做了賦值
            //上一篇realStartServiceLocked方法沒有觸發(fā)requestServiceBindingsLocked
            //但是這里就會不一樣了.
            //需要注意的是误债,這里pendingStarts會為空浸船,所以后面的sendServiceArgsLocked不觸發(fā)
            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent,
                    callerApp.uid, callerApp.processName, callingPackage);

            IBinder binder = connection.asBinder();
            s.addConnection(binder, c);
            b.connections.add(c);
            if (activity != null) {
                activity.addConnection(c);
            }
            b.client.connections.add(c);
            c.startAssociationIfNeeded();
            ...
            //我們知道妄迁,如果需要Service不在運(yùn)行狀態(tài)的時(shí)候觸發(fā),需要設(shè)置標(biāo)志Context.BIND_AUTO_CREATE

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

        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        return 1;
    }

和上一篇的realStartServiceLocked行為不同的是李命,因?yàn)樯厦尜x值了bindings登淘,所以在Service的onCreate后,會觸發(fā)scheduleBindService而不再是上篇的scheduleServiceArgs封字。
ActiveServices

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        //Service的進(jìn)程拉起和onCreate觸發(fā)不再復(fù)述
        //請看上一篇
        ...
        //這里由于上面對bindings做了賦值黔州,所以能觸發(fā)
        requestServiceBindingsLocked(r, execInFg);

        //pendingStarts會為空,所以什么都沒做
        sendServiceArgsLocked(r, execInFg, true);

        ...
    }  

    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 {
        ...
            try {
                ...
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.getReportedProcState());
              
            }
        ...
    }  

熟悉的ApplicationThread->H->ActivityThread三連招阔籽。
ApplicationThread

        public final void scheduleBindService(IBinder token, Intent intent,
                boolean rebind, int processState) {
            updateProcessState(processState, false);
            BindServiceData s = new BindServiceData();
            s.token = token;
            s.intent = intent;
            s.rebind = rebind;

            if (DEBUG_SERVICE)
                Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                        + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
            sendMessage(H.BIND_SERVICE, s);
        }

H

                case BIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                    handleBindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

ActvityThread

    private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (DEBUG_SERVICE)
            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    //與StartService不同的是流妻,這里會調(diào)用
                    //AMS的publishService
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to bind to service " + s
                            + " with " + data.intent + ": " + e.toString(), e);
                }
            }
        }
    }

AMSpublishService行為被觸發(fā)。
上面可以看到笆制,Service已經(jīng)創(chuàng)建并且調(diào)用onCreate方法绅这。下面我們看下scheduleServiceArgs做了什么。
ActivityManagerService

    public void publishService(IBinder token, Intent intent, IBinder service) {
        // Refuse possible leaked file descriptors
        if (intent != null && intent.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        synchronized(this) {
            if (!(token instanceof ServiceRecord)) {
                throw new IllegalArgumentException("Invalid service token");
            }
            mServices.publishServiceLocked((ServiceRecord)token, intent, service);
        }
    }

ActiveServices

    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        ...
                            try {
                                //這里是之前傳入的IServiceConnection對象
                                //所以我們直接往回找就好
                                c.conn.connected(r.name, service, false);
                            }
        ...
    }

LoadedApk里项贺,可以找到類InnerConnection君躺,看它的connected行為峭判。
InnerConnection

            public void connected(ComponentName name, IBinder service, boolean dead)
                    throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service, dead);
                }
            }

ServiceDispatcher

        public void connected(ComponentName name, IBinder service, boolean dead) {
            if (mActivityThread != null) {
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else {
                doConnected(name, service, dead);
            }
        }

        public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ...
            // If there is a new viable service, it is now connected.
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            } else {
                // The binding machinery worked, but the remote returned null from onBind().
                mConnection.onNullBinding(name);
            }
        }

一路找下來开缎,其實(shí)就是把之前緩存的ServiceConnection,觸發(fā)回調(diào)onServiceConnected林螃。

這篇的話奕删,是基于Q的代碼看bindService的行為流程。這方面android P和Android Q基本沒有差異疗认。
整個流程的話完残,和上篇startService類似。不同的是因?yàn)槌跏蓟腟erviceRecord的參數(shù)不同横漏,所以觸發(fā)了bind谨设,后面publishService操作,主要是為了向發(fā)起bind的進(jìn)程傳遞binder信息缎浇。這個的話按圖理解流程扎拣,應(yīng)該來說還是相當(dāng)清晰的。
Service篇的話就這兩篇素跺,startService和bindService二蓝。Service其他相關(guān)的當(dāng)前應(yīng)當(dāng)不會寫相關(guān)博客。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末指厌,一起剝皮案震驚了整個濱河市刊愚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌踩验,老刑警劉巖鸥诽,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件商玫,死亡現(xiàn)場離奇詭異,居然都是意外死亡牡借,警方通過查閱死者的電腦和手機(jī)决帖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蓖捶,“玉大人地回,你說我怎么就攤上這事】∮悖” “怎么了刻像?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長并闲。 經(jīng)常有香客問我细睡,道長,這世上最難降的妖魔是什么帝火? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任溜徙,我火速辦了婚禮,結(jié)果婚禮上犀填,老公的妹妹穿的比我還像新娘蠢壹。我一直安慰自己,他們只是感情好九巡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布图贸。 她就那樣靜靜地躺著,像睡著了一般冕广。 火紅的嫁衣襯著肌膚如雪疏日。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天撒汉,我揣著相機(jī)與錄音沟优,去河邊找鬼。 笑死睬辐,一個胖子當(dāng)著我的面吹牛挠阁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播溉委,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鹃唯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瓣喊?” 一聲冷哼從身側(cè)響起坡慌,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎藻三,沒想到半個月后洪橘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跪者,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年熄求,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了渣玲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡弟晚,死狀恐怖忘衍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卿城,我是刑警寧澤枚钓,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站瑟押,受9級特大地震影響搀捷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜多望,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一嫩舟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧怀偷,春花似錦家厌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晋渺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間脓斩,已是汗流浹背木西。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留随静,地道東北人八千。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像燎猛,于是被迫代替她去往敵國和親恋捆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容