Binder機(jī)制揭秘

前言

最近自己在整理一些東西,總歸吧學(xué)到的記錄下來這樣才能更好的理解纸镊,方便以后查閱倍阐。如果文章哪里理解的有些偏差,還望大家指正逗威。

什么是Binder

  • Binder是一種Android中實(shí)現(xiàn)跨進(jìn)城通信的方式峰搪,是Android為了跨進(jìn)程提出來的,是Android特有的凯旭。
  • 從組成上來說概耻,Binder是一種虛擬的物理設(shè)備驅(qū)動(dòng)使套。
  • 從Android 代碼來說,Binder是一個(gè)類鞠柄,實(shí)現(xiàn)IBinder接口侦高,是將Binder機(jī)制模型以代碼的形式實(shí)現(xiàn)在整個(gè)Android系統(tǒng)中。

為什么用Binder

我們都知道android是基于Linux開發(fā)的厌杜,那么為什么不用Linux的奉呛,而是要自己定義一套呢。那么Google沒用那肯定說明Linux下的消息通信機(jī)制是不適合android的夯尽,那Linux下有哪幾種瞧壮?為什么不能用?一般都是基于性能呐萌、穩(wěn)定性馁痴、安全性

Linux下的消息通信

主要有 socket管道肺孤、消息隊(duì)列罗晕、共享內(nèi)存

性能:首先socket是基于文件和端口通信,傳輸效率低赠堵,開銷大小渊,一般用于跨網(wǎng)絡(luò)之間的進(jìn)程間通信,所以這個(gè)肯定pass掉了茫叭。而管道和消息隊(duì)列采用了存儲(chǔ)-轉(zhuǎn)發(fā)的方式:即先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)酬屉,再從內(nèi)核開辟的緩存區(qū)發(fā)送到接收方的緩存區(qū),整個(gè)過程至少有兩次拷貝揍愁,那肯定pass呐萨。而共享內(nèi)存雖然無需拷貝,但是控制復(fù)雜莽囤,難以使用谬擦。那Binder只需要一次拷貝,性能僅次于共享內(nèi)存朽缎。

穩(wěn)定性:Binder是基于C/S架構(gòu)惨远,客戶端又什么需求直接丟給服務(wù)端服務(wù)端區(qū)完成,架構(gòu)清晰话肖,職責(zé)明確北秽,那么肯定性能,雖然共享內(nèi)存無需拷貝最筒,但是控制負(fù)責(zé)贺氓,肯定沒有Binder穩(wěn)定。

安全性:那么Android作為一個(gè)開放性的平臺(tái)床蜘,有大量的應(yīng)用給用戶讓用戶使用掠归,因此安全性對(duì)于Android平臺(tái)來說很重要的缅叠,作為用戶來說肯定不希望你這些應(yīng)用把我手機(jī)上個(gè)人隱私信息全部偷走,在后臺(tái)各種亂來虏冻,而傳統(tǒng)的IPC是沒有任何安全措施的肤粱,完全依賴于上層。而Binder是由系統(tǒng)給每個(gè)應(yīng)用在內(nèi)核空間添加一個(gè)UIP/PID來添加身份標(biāo)示的厨相。所以Binder的安全性很高领曼。

而Binder可以建立私有管道,是Linux的通信機(jī)制所無法實(shí)現(xiàn)的蛮穿。

Binder實(shí)現(xiàn)原理

內(nèi)存映射:Binder的跨進(jìn)程通信和傳統(tǒng)的Linux跨進(jìn)程通信不一樣庶骄,它是利用了Linux下的一個(gè)內(nèi)存映射這個(gè)概念來實(shí)現(xiàn)的,也就是mmap()践磅。內(nèi)存映射就是將用戶空間中的一塊內(nèi)存區(qū)域映射到內(nèi)核空間单刁,映射關(guān)系建立后,用戶對(duì)這塊內(nèi)存的修改直接可以反應(yīng)到內(nèi)核空間,反之內(nèi)核空間對(duì)這段區(qū)域的修改也能直接反應(yīng)到用戶空間。而且內(nèi)存映射相對(duì)管道和消息隊(duì)列來說少了一次拷貝泌辫,實(shí)現(xiàn)了用戶空間和內(nèi)核空間兩個(gè)及時(shí)感知养筒。

一次通信過程

1讲坎、首先Binder驅(qū)動(dòng)在內(nèi)核空間創(chuàng)建一個(gè)數(shù)據(jù)接收緩存區(qū)

2、接著在內(nèi)核空間開辟一塊內(nèi)核緩存區(qū),建立內(nèi)核緩存區(qū)和內(nèi)核中數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系卡儒,以及內(nèi)核中數(shù)據(jù)接收緩存區(qū)接收進(jìn)程用戶空間地址的映射關(guān)系。

3俐巴、發(fā)送方通過系統(tǒng)調(diào)用將數(shù)據(jù)copy到內(nèi)核中的內(nèi)核緩存區(qū)骨望,由于內(nèi)核緩存區(qū)接收進(jìn)城的用戶空間有映射關(guān)系, 因此 相當(dāng)于吧數(shù)據(jù)發(fā)送到了接收進(jìn)城的用戶空間欣舵,這樣就完成了一次通信擎鸠。

從代碼角度看通信過程

  1. 首先,一個(gè)進(jìn)程使用BINDER_SEZT_CONTEXT_MGR命令通過Binder驅(qū)動(dòng)將自己注冊(cè)成為了ServiceManager
  2. Server通過驅(qū)動(dòng)向ServiceManager中注冊(cè)Binder(Server中Binder實(shí)體)邻遏,驅(qū)動(dòng)為這個(gè)Binder創(chuàng)建位于內(nèi)核中實(shí)體節(jié)點(diǎn)以及ServerManager對(duì)實(shí)體的引用糠亩,將名字以及新建的引用包打包傳給ServiceManager虐骑,ServiceManager將起填入查找表
  3. Client通過名字准验,在Binder驅(qū)動(dòng)的幫助下蔥ServiceManager中獲取對(duì)Binder實(shí)體的引用,通過這個(gè)引用就能完成和Server的通信

先來一個(gè)Binder通訊的流程圖
Binder通信流程圖.png

Binde機(jī)制在Android中的具體實(shí)現(xiàn)原理

在我們Android中廷没,首先要定義一個(gè)IMyService的AIDL文件糊饱,然后會(huì)幫我生成一個(gè)java文件,內(nèi)部會(huì)有Stub和Proxy類颠黎,他們分別是Binder和BinderProxy的子類另锋,Stub和Proxy都是實(shí)現(xiàn)了這個(gè)接口滞项。

所以這個(gè)IInterface到底是什么,它呢就是一個(gè)用于表達(dá)Service提供的功能的一個(gè)契約夭坪,也就是說IInterface里有的方法文判,Service都能提供,調(diào)用者不用管BinderProxy是什么室梅,只要拿到Interface戏仓,就可以直接調(diào)用里面的方法。

Proxy實(shí)現(xiàn)了IMyService亡鼠,并且實(shí)現(xiàn)了里面的方法赏殃。為什么IMyservice要分Stub和Proxy,這是為了適用于本地調(diào)用和遠(yuǎn)程調(diào)用兩種情況间涵,如果Service運(yùn)行在同一個(gè)進(jìn)程仁热,那直接就用Stub,因?yàn)樗侵苯訉?shí)現(xiàn)了Serice提供的功能勾哩,不需要任何IPC過程抗蠢,如果Service運(yùn)行在其他進(jìn)程,哪么客戶端就使用的Proxy钳幅,就是吧參數(shù)封裝后發(fā)送給Binder物蝙,然后Binder轉(zhuǎn)發(fā)給ServiceManager從而讓對(duì)應(yīng)的Server段執(zhí)行 在將結(jié)果返回。

bindService源碼解析

那么如果我們客戶端要去連接Server敢艰,那么一般都是要bindService去綁定的我們的服務(wù)诬乞,我們會(huì)去傳一個(gè)ServiceConnection,下面分析下源碼看看bindService 這一步是如何通過Binder去完成跨進(jìn)程綁定我們的服務(wù)的钠导,并且返回的Binder對(duì)象的

先肯定是bindService了震嫉,這里調(diào)用的ContextWrapper,那么它的實(shí)現(xiàn)類是ContextImpl牡属,而且僅有這一個(gè)實(shí)現(xiàn)類

ContextImpl類

@Override
public boolean bindService(Intent service, ServiceConnection conn,int flags) {
    // 如果是系統(tǒng)進(jìn)程會(huì)打印一個(gè)log
    warnIfCallingFromSystemProcess();
    // 又去調(diào)用bindServiceCommon 這個(gè)方法
    return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
}

/**
** 這里的conn 就是我們傳過來要接受IBinder的接口
*/
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
            IServiceConnection sd;
      if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        // mPackageInfo是loadApk的實(shí)例
        // handler是ActivityThread的mH實(shí)例票堵,將它保存到ServiceDispatcher里面,得到了一個(gè)IServiceConnect接口
      if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
      } else {
            throw new RuntimeException("Not supported in system context");
       }
       ...
     
       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);
            }


}

從上面得出逮栅,要執(zhí)行ActivityManager.getService() 得到的這個(gè)對(duì)象的bindService方法悴势,那么我們就看看ActivityManager的getService方法。

ActivityManager類
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

從上面看出措伐,得到的一個(gè)IActivityManager而且是通過通過IBinder進(jìn)行轉(zhuǎn)換的

public interface IActivityManager extends IInterface {
}

那么這里是基于Android8.0 (26)26的源碼特纤,26以下是和這里不一樣的,26用AIDL通信侥加,(26以下其實(shí)是中間多了一層代理對(duì)象)捧存。 那么就是得到了遠(yuǎn)程ActivityManagerService這個(gè)對(duì)象,那么我們直接看它是怎么實(shí)現(xiàn)的

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

        synchronized(this) {
          //這里調(diào)用的ActivitySerivces這個(gè)的bindServiceLocked
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, callingPackage, userId);
        }
    }

ActivityServices類

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {
  
  ...
    //從ServiceMap中查找ServiceRecord對(duì)象
    ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
   ServiceRecord s = res.record;
  ...
    // 創(chuàng)建鏈接信息
    AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent);
    
  //將創(chuàng)建好的連接信息存到集合中
  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 ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                b.client.hasAboveClient = true;
            }
            if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
                s.whitelistManager = true;
            }
            if (s.app != null) {
                updateServiceClientActivitiesLocked(s.app, c, true);
            }
            clist = mServiceConnections.get(binder);
            if (clist == null) {
                clist = new ArrayList<ConnectionRecord>();
                mServiceConnections.put(binder, clist);
            }
            clist.add(c);
  
  
  //如果Flag中包含BIND_AUTO_CREATE就啟動(dòng)Service
  if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                        permissionsReviewRequired) != null) {
                    return 0;
                }
            }
  
  
  if (s.app != null && b.intent.received) {
//如果Service已經(jīng)啟動(dòng)或者這個(gè)intent沒有被連接過,立即連接昔穴,在會(huì)掉onServiceConnected
     c.conn.connected(s.name, b.intent.binder, false);
       
   } else if (!b.intent.requested) {
    //Service啟動(dòng)了 但是沒有bind過镰官,先調(diào)用onBind,在會(huì)掉onServiceConnected
     requestServiceBindingLocked(s, b.intent, callerFg, false);
  }
}

源碼就分析到這里吧吗货,下面總結(jié)下

  1. 當(dāng)我們bindService的時(shí)候泳唠,會(huì)調(diào)用ContextImpl的bindService的方法,然后獲取一個(gè)ServiceDispath.InnerConnect的對(duì)象宙搬,可以跨進(jìn)程通信
  2. 然后通過Binder回去調(diào)用ActivityManagerService的bindService 的方法警检,然乎會(huì)判斷當(dāng)前服務(wù)有沒有啟動(dòng)和綁定。
  3. 如果沒有啟動(dòng)害淤,那就執(zhí)行realStartServiceLocked的方法扇雕,在去調(diào)用ActivityThread中的ApplicationThread中的scheduleCreateService方法,通過反射加載一個(gè)class窥摄,去執(zhí)行onCreate方法镶奉。
  4. 如果啟動(dòng)但是沒有bind,執(zhí)行requestServiceBiningsLocked方法崭放,在里面調(diào)用ActivityThread中的ApplicationThread中的scheduleBindService方法來執(zhí)行service的bind過程哨苛。
  5. 然后調(diào)用AMS的publishService,再去調(diào)用我們的ServiceDispatch.InnerConnection的connect方法

大概就寫完了币砂,算是自己的學(xué)習(xí)筆記吧建峭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市决摧,隨后出現(xiàn)的幾起案子亿蒸,更是在濱河造成了極大的恐慌,老刑警劉巖掌桩,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件边锁,死亡現(xiàn)場離奇詭異,居然都是意外死亡波岛,警方通過查閱死者的電腦和手機(jī)茅坛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來则拷,“玉大人贡蓖,你說我怎么就攤上這事』筒纾” “怎么了斥铺?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宣旱。 經(jīng)常有香客問我仅父,道長,這世上最難降的妖魔是什么浑吟? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任笙纤,我火速辦了婚禮,結(jié)果婚禮上组力,老公的妹妹穿的比我還像新娘省容。我一直安慰自己,他們只是感情好燎字,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布腥椒。 她就那樣靜靜地躺著,像睡著了一般候衍。 火紅的嫁衣襯著肌膚如雪笼蛛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天蛉鹿,我揣著相機(jī)與錄音滨砍,去河邊找鬼。 笑死妖异,一個(gè)胖子當(dāng)著我的面吹牛惋戏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播他膳,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼响逢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了棕孙?” 一聲冷哼從身側(cè)響起舔亭,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蟀俊,沒想到半個(gè)月后分歇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡欧漱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年职抡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片误甚。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缚甩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窑邦,到底是詐尸還是另有隱情擅威,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布冈钦,位于F島的核電站郊丛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厉熟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一导盅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧揍瑟,春花似錦白翻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至底循,卻和暖如春巢株,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背熙涤。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工纯续, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人灭袁。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓猬错,卻偏偏與公主長得像,于是被迫代替她去往敵國和親茸歧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子倦炒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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