bindService源碼流程

前沿

上一篇文章給大家分析了一下Binder的交互流程,這篇文章分析一下上篇遺留的bindService執(zhí)行流程傅蹂,當我們調(diào)用了bindService系統(tǒng)為我們做了些什么纷闺。

首先算凿,當我們想要去綁定一個遠程service時,我們需要寫以下代碼:

    Intent intent = new Intent(this,MyService.class);
    bindService(intent,connection,BIND_AUTO_CREATE);

這時候我們就要進入bindService

    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }
    public abstract boolean bindService(Intent service, ServiceConnection conn,
            int flags);

這時候我們發(fā)現(xiàn)這個調(diào)用了mBase.bindService.進入這個方法才發(fā)現(xiàn)是個抽象方法犁功。我去氓轰,那怎么辦,我們需要找到他真正的子類實現(xiàn)浸卦,這里就不給大家賣關子了戒努,我們真正的子類實現(xiàn)是ContextImpl這個類,那我們?nèi)タ匆幌逻@個類中的bindService吧镐躲。

    @Override                                                                         
    public boolean bindService(Intent service, ServiceConnection conn,                
            int flags) {                                                              
        warnIfCallingFromSystemProcess();                                             
        return bindServiceCommon(service, conn, flags, Process.myUserHandle());       
    }                                                                                 
 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,   
         UserHandle user) {                                                             
     IServiceConnection sd;                                                                                                                                          
     if (mPackageInfo != null) { 
         //這里先記錄以下,一會還會回來                                                    
         sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),                
                 mMainThread.getHandler(), flags);                                      
     }                                                                               
     validateServiceIntent(service);                                                    
     try {                                                                              
         IBinder token = getActivityToken();                                            
         //省略一些代碼侍筛。                                                           
         service.prepareToLeaveProcess();                                               
         int res = ActivityManagerNative.getDefault().bindService(                      
             mMainThread.getApplicationThread(), getActivityToken(),                    
             service, service.resolveTypeIfNeeded(getContentResolver()),                
             sd, flags, user.getIdentifier());                                          
         if (res < 0) {                                                                 
             throw new SecurityException(                                               
                     "Not allowed to bind to service " + service);                      
         }                                                                              
         return res != 0;                                                               
     } catch (RemoteException e) {                                                      
         return false;                                                                  
     }                                                                                  
 }                                                                                      

這時候就會調(diào)用ActivityManagerNative.getDefault().bindService這個ActivityManagerNative.getDefault()代碼如下:

    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

通過ServiceManager 去獲取了一個binder萤皂,并把這個binder返回了,是一個IActivityManager如果大家看過很多底層代碼就會很熟悉這樣的接口類匣椰,我們通過這個接口可以猜出其子類是ActivityManagerService裆熙。我們?nèi)ミ@個類中的bindService一探究竟。

public int bindService(IApplicationThread caller, IBinder token,
        Intent service, String resolvedType,
        IServiceConnection connection, int flags, int userId) {
    synchronized(this) {
        return mServices.bindServiceLocked(caller, token, service, resolvedType,
                connection, flags, userId);
    }
}

調(diào)用了bindServiceLocked方法

// 省略掉一些有關Activity的啟動流程禽笑,我們再后面再說
   private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        if (app.thread == null) {
            throw new RemoteException();
        }
        requestServiceBindingsLocked(r, execInFg);
   }

最后會調(diào)用requestServiceBindingsLocked方法

private final boolean requestServiceBindingLocked(ServiceRecord r,
        IntentBindRecord i, boolean execInFg, boolean rebind) {
    if (r.app == null || r.app.thread == null) {
        // If service is not currently running, can't yet bind.
        return false;
    }
    if ((!i.requested || rebind) && i.apps.size() > 0) {
        try {
            bumpServiceExecutingLocked(r, execInFg, "bind");
            r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                    r.app.repProcState);
            if (!rebind) {
                i.requested = true;
            }
            i.hasBound = true;
            i.doRebind = false;
        } catch (RemoteException e) {
            if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
            return false;
        }
    }
    return true;
}

這里我們看r.app.thread.scheduleBindService這個方法入录,但是你又會問,點不進去啊佳镜,這個scheduleBindService在哪里呢僚稿。我們進到ProcessRecord里面看一下

final class ProcessRecord {
    private final BatteryStatsImpl mBatteryStats; // where to collect runtime statistics
    final ApplicationInfo info; // all about the first app in the process
    final boolean isolated;     // true if this is a special isolated process
    final int uid;              // uid of process; may be different from 'info' if isolated
    final int userId;           // user of process.
    final String processName;   // name of the process
    // List of packages running in the process
    final ArrayMap<String, ProcessStats.ProcessState> pkgList
            = new ArrayMap<String, ProcessStats.ProcessState>();
    IApplicationThread thread;

發(fā)現(xiàn)又是一個IApplicationThread那我們的實現(xiàn)類是是就可以猜出是ApplicationThread這個類呢,其實這樣想我們是對的蟀伸,但是在這里你是找不到這個類的蚀同,我在開始找的時候也很頭疼,找了半天發(fā)現(xiàn)不是我所想的那樣啊掏。這里的ApplicationThread是一個內(nèi)部類蠢络,它在ActivityThread中。讓我們繼續(xù)走剛剛的方吧迟蜜。

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;
    Binder.getCallingPid());
    sendMessage(H.BIND_SERVICE, s);
}

這里通過H 發(fā)送了一個BIND_SERVICE消息

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

調(diào)用了handleBindService方法

private void handleBindService(BindServiceData data) {
    //從記錄中獲取一個service對象刹孔,每次啟動Service,系統(tǒng)都會記錄到mServices中
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            data.intent.setExtrasClassLoader(s.getClassLoader());
            try {
                //判斷service是否未綁定過了
                if (!data.rebind) {
                    //沒有綁定需要走onBind
                    IBinder binder = s.onBind(data.intent);
                    ActivityManagerNative.getDefault().publishService(
                            data.token, data.intent, binder);
                } else {
                    //綁定過需要走onRebind
                    s.onRebind(data.intent);
                    ActivityManagerNative.getDefault().serviceDoneExecuting(
                            data.token, 0, 0, 0);
                }
                ensureJitEnabled();
            } catch (RemoteException ex) {
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(s, e)) {
                throw new RuntimeException(
                        "Unable to bind to service " + s
                        + " with " + data.intent + ": " + e.toString(), e);
            }
        }
    }
}

這里會判斷是否已經(jīng)綁定過了娜睛,如果未綁定就回調(diào)onBind方法髓霞,綁定過了就會回調(diào)onRebingd方法。最后會調(diào)用publishService方法微姊,我們在前面見過這個類酸茴,而且知道他的子類是ActivityManagerService,那我們?nèi)ダ锩婵纯窗伞?/p>

public void publishService(IBinder token, Intent intent, IBinder service) {
    synchronized(this) {
        mServices.publishServiceLocked((ServiceRecord)token, intent, service);
    }
}

調(diào)用了publishServiceLocked這里我們只貼關鍵代碼

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)) {
            if (DEBUG_SERVICE) Slog.v(
                    TAG, "Not publishing to: " + c);
            if (DEBUG_SERVICE) Slog.v(
                    TAG, "Bound intent: " + c.binding.intent.intent);
            if (DEBUG_SERVICE) Slog.v(
                    TAG, "Published intent: " + intent);
            continue;
        }
        if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
        try {
            c.conn.connected(r.name, service);
        } catch (Exception e) {
            Slog.w(TAG, "Failure sending service " + r.name +
                  " to connection " + c.conn.asBinder() +
                  " (in " + c.binding.client.processName + ")", e);
        }
    }
}

這時候就會去ConnectionRecord list中去查找,找到就開始調(diào)用c.conn.connected(r.name, service)兢交,那這個conn又是什么呢薪捍,我們點進去看一下。

final class ConnectionRecord {
    final IServiceConnection conn; 

是不是感覺很熟悉,那我們按照以前的套路去查一下ServiceConnection這個類吧酪穿,但是你會失望的發(fā)現(xiàn)他還是個接口凳干,那咋辦,還記得我在上面有個地方說一會還會回來看這里的地方么被济。我們?nèi)タ纯础?/p>

 if (mPackageInfo != null) {                                            
     sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),    
             mMainThread.getHandler(), flags);                          
 }                                                             

發(fā)現(xiàn)進入到了LoadedApk中的getServiceDispatcher

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) {
            sd = map.get(c);
        }
        if (sd == null) {
            sd = new ServiceDispatcher(c, context, handler, flags);
            if (map == null) {
                map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                mServices.put(context, map);
            }
            map.put(c, sd);
        } else {
            sd.validate(context, handler);
        }
        return sd.getIServiceConnection();
    }
}

這里把LoadedApk.ServiceDispatcher放進了一個map中救赐,這里就是我們上面要找的那個conn,讓我們看一下這個類的實現(xiàn)吧。

private static class InnerConnection extends IServiceConnection.Stub {
    final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

    InnerConnection(LoadedApk.ServiceDispatcher sd) {
        mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
    }

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

public void connected(ComponentName name, IBinder service) {
        doConnected(name, service);
}

public void doConnected(ComponentName name, IBinder service) {
    if (service != null) {
        mConnection.onServiceConnected(name, service);
    }
}

經(jīng)過一連串的方法調(diào)用只磷,終于看到了我們認識的方法mConnection.onServiceConnected(name, service);
最后會吧IBinder回調(diào)到我們的客戶端经磅。到這里bindService的流程就完了。

UML圖如下:
bindService.jpg

喜歡的請大家點贊哦钮追!不對的地方請留言指出预厌,謝謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末元媚,一起剝皮案震驚了整個濱河市轧叽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刊棕,老刑警劉巖炭晒,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異甥角,居然都是意外死亡网严,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門嗤无,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屿笼,“玉大人,你說我怎么就攤上這事翁巍÷恳唬” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵灶壶,是天一觀的道長肝断。 經(jīng)常有香客問我,道長驰凛,這世上最難降的妖魔是什么胸懈? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮恰响,結(jié)果婚禮上趣钱,老公的妹妹穿的比我還像新娘。我一直安慰自己胚宦,他們只是感情好首有,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布燕垃。 她就那樣靜靜地躺著,像睡著了一般井联。 火紅的嫁衣襯著肌膚如雪卜壕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天烙常,我揣著相機與錄音轴捎,去河邊找鬼。 笑死蚕脏,一個胖子當著我的面吹牛侦副,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播驼鞭,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼跃洛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了终议?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤葱蝗,失蹤者是張志新(化名)和其女友劉穎穴张,沒想到半個月后漓帅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體憔古,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年供常,在試婚紗的時候發(fā)現(xiàn)自己被綠了悼凑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偿枕。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖户辫,靈堂內(nèi)的尸體忽然破棺而出渐夸,到底是詐尸還是另有隱情,我是刑警寧澤渔欢,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布墓塌,位于F島的核電站,受9級特大地震影響奥额,放射性物質(zhì)發(fā)生泄漏苫幢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一垫挨、第九天 我趴在偏房一處隱蔽的房頂上張望韩肝。 院中可真熱鬧,春花似錦九榔、人聲如沸哀峻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谜诫。三九已至漾峡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間喻旷,已是汗流浹背生逸。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留且预,地道東北人槽袄。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像锋谐,于是被迫代替她去往敵國和親遍尺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

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