通過service分析安卓binder機制的進程間通信

安卓中進程間通信大家可能都非常了解胞谭,我們知道service可以使用aidl方法贞瞒,遠程調(diào)用服務(wù)實現(xiàn)兩個不同app的通信。根據(jù)文檔也能很快實現(xiàn)功能仇哆,但具體是怎么跨越兩個進程進行通信的靶衍,現(xiàn)在跟著我的腳步分析分析吧灾炭。
首先提出產(chǎn)生困惑的幾個問題:

  1. 哪些代碼是在哪些進程或線程中運行的。
  2. 是如何傳遞數(shù)據(jù)的颅眶。
  3. 是如何傳遞方法蜈出。

代碼角度分析

server端

首先service通過binder機制的通信是屬于server-client模式的,server端為Service類涛酗,在Service中需要實現(xiàn)onBind方法铡原,該方法返回實現(xiàn)了IBinder的實例,這里類實例也通常是在Service中實現(xiàn)的商叹。

 @Override
    public IBinder onBind(Intent intent) {
        initSum = intent.getIntExtra("initSum", -1);
        Log.d("jw", "onBind: Thread:"+Thread.currentThread().getId());
        return remoteService;
    }

    IBinder remoteService = new IMyAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
        
        }
        
        @Override
        public int add(int a, int b) throws RemoteException {
            Log.d("jw", "add thread: "+Thread.currentThread().getId());
            return a+b+initSum;
        }
   }

client端

在client端燕刻,示例代碼如下:

ServiceConnection mAddServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            addService = IAddAidlInterface.Stub.asInterface(service);

            serviceConnected = true;
            tv.setText("bind success");

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            serviceConnected =false;
        }
    };

    public void bindAddService(View view) {
        Intent intent = new Intent();
        intent.setClassName("com.yglx.learnservice", "com.yglx.learnservice.MyService");
        bindService(intent, mAddServiceConnection, BIND_AUTO_CREATE);
        Log.d("jw", "bindAddService thread: "+Thread.currentThread().getId());
    }

客戶端在和服務(wù)器連接成功后,拿到了一個實現(xiàn)了某些接口(即客戶端和服務(wù)器端端aidl生成的
接口)的實例剖笙,之后只需要用這個實例就可以訪問相應(yīng)的接口了卵洗。

交互

可以看出直接使用不是很難,咋一看就是這么一回事的:
1弥咪, 客戶端bindservice过蹂。
2, 神秘的力量判斷這個service是否有binder聚至,如果沒有,代碼執(zhí)行3酷勺。如果有binder直接運行4。
3扳躬, 服務(wù)端運行onbind返回一個binder脆诉。
4勋功, 又一股神秘的力量,把binder拿過來库说,讓代碼運行5。
5片择, 客戶端運行ServiceConnection中的回調(diào)函數(shù)潜的,把binder給到客戶端。
6字管, 客戶端愉快的開始通過那個binder和服務(wù)端通信了啰挪。
7, 客戶端運行binder的相關(guān)方法嘲叔,實際應(yīng)該是一個寫入虛擬內(nèi)存的過程亡呵。
8, 內(nèi)核的神秘力量又弄出一個線程來硫戈,運行服務(wù)端端代碼锰什,即告訴服務(wù)端可以讀虛擬內(nèi)存的數(shù)
據(jù)了,然后返回到內(nèi)核丁逝。
9汁胆, 內(nèi)核的神秘力量又把給到的數(shù)據(jù),讓剛才客戶端的線程繼續(xù)運行下去霜幼。
10嫩码,客戶端和服務(wù)器就圓滿完成了一次跨進程通信了。
以上流程基本就說明了罪既,在應(yīng)用層進程間是如何通信的铸题。

進程間都有哪些線程

先講我們最熟悉的主線程。(這里為了讓大家看清都涉及到的線程琢感,所以假設(shè)服務(wù)端和客戶端都在代碼上都沒有顯示的開啟線程)

  1. 當客戶端調(diào)用binderSevice方法時丢间,這個時候處于客戶端主線程。
  2. 如果service沒有啟動猩谊,則在service的oncreate生命周期中為服務(wù)端主線程千劈。
  3. 如果未有binder綁定過,則服務(wù)端回調(diào)生命周期方法onbind為服務(wù)端主線程牌捷。
  4. 客戶端回調(diào)ServiceConnection的方法墙牌,這個也是客戶端主線程。
  5. 客戶端發(fā)起binder的相關(guān)方法暗甥,這個時候在客戶端主線程喜滨。
  6. 這時服務(wù)端要開始運行協(xié)議中的指定的binder方法了,這里要分兩種情況撤防,一種是如果客戶端和服務(wù)端在同一個進程虽风,則此時運行的協(xié)議方法也是和客戶端一樣的線程;如果客戶端和服務(wù)
    端不在同一個進程,則服務(wù)端將會有一個新的線程來執(zhí)行協(xié)議的指定方法辜膝。
  7. 客戶端得到binder相關(guān)方法端返回值无牵,如果有返回值的話,這里是同步進行的厂抖,即客戶端將阻塞直到服務(wù)端返回數(shù)據(jù)茎毁,這里還是客戶端的主線程(所以客戶端運行遠程調(diào)用方法時,不應(yīng)該在主線程中執(zhí)行)忱辅。

到這里七蜘,第一個問題解決。

傳遞數(shù)據(jù)

說傳遞數(shù)據(jù)的時候墙懂,先簡單說一下自動生成的aidl文件橡卤。
接口文件中有一個繼承了binder的抽象類Stub,Stub里有一個實現(xiàn)了接口的Proxy代理類损搬。這里我注意到一點碧库,其實我們不管在客戶端還是服務(wù)端都用的同一個aidl文件生成的,并且放在aidl文件的包必須一致才不會出錯巧勤。在我的測試代碼中谈为,為了在接口文件中能獲取一些日志信息,所以我直接把在服務(wù)端生成的接口文件拷貝到測試的應(yīng)用工程中踢关。在對日志分析時伞鲫,注意到,這個文件其實是有兩個功能的签舞,一個是用于服務(wù)端的主要是抽象類Stub秕脓,另一個是在客戶端應(yīng)用中使用的Proxy代理類。所以在服務(wù)器端自己不會作為客戶端的話儒搭,可以把Proxy類去除吠架。如果客戶端不會用作服務(wù)器的話,也完全可以把Stub去除搂鲫,把Proxy類抽離出來就可以傍药。
數(shù)據(jù)傳輸中,并不是所有數(shù)據(jù)都能傳輸?shù)幕耆裕嘶绢愋屯夤樟桑瑒t是需要實現(xiàn)了parcel的可序列化的類才能傳遞,和Intent傳輸?shù)臄?shù)據(jù)類似(Intent其實也是一種進程間通信數(shù)據(jù)傳輸?shù)妮d體)擦酌。

  1. 客戶端調(diào)用binder的協(xié)議方法時俱诸,運行Proxy類中的方法。
    比如demo中的add方法:
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();

流程是把數(shù)據(jù)的寫入和讀出都是通過Parcel赊舶,這也就是類需要實現(xiàn)Parcel的原因睁搭。

  1. 客戶端調(diào)用mRetome.transact之后赶诊,經(jīng)過底層binder的神秘力量,將會調(diào)用服務(wù)端transact方法园骆,即服務(wù)端的Stub里的onTransact方法:
case TRANSACTION_add: {
    data.enforceInterface(DESCRIPTOR);
    Log.d("jw", "aidl stub onTransact: Thread:"+Thread.currentThread().getId());
    int _arg0;
    _arg0 = data.readInt();
    int _arg1;
    _arg1 = data.readInt();
    int _result = this.add(_arg0, _arg1);
    reply.writeNoException();
    reply.writeInt(_result);
    return true;
}

可以看到是先從data中讀取底層傳輸過來的數(shù)據(jù)舔痪,然后取出對應(yīng)的數(shù)據(jù),調(diào)用相應(yīng)的服務(wù)端已經(jīng)實現(xiàn)了的this.add方法锌唾,用reply把數(shù)據(jù)寫回底層binder辙喂。
好了,數(shù)據(jù)傳輸流程就這樣的鸠珠。

傳遞方法

傳遞方法即傳遞回調(diào)方法(本質(zhì)上傳的是一個binder,如同binderService返回一個binder)秋麸,>即有的時候需要服務(wù)端主動發(fā)消息渐排,能讓客戶端收到。這個時候就可以通過客戶端把回調(diào)接口注冊到服務(wù)端中灸蟆,服務(wù)端通過RemoteCallbackList把這些接口(binder)保存起來驯耻,當需要向客戶端發(fā)送消息的時候即可遍歷里面的接口把數(shù)據(jù)通知過去。在回調(diào)的時候其實就相當于客戶端這個時候充當了服務(wù)端炒考。
客戶端把binder寫入到data中

_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((messageReceiver != null)) ? (messageReceiver.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_registerReceiver, _data, _reply, 0);
_reply.readException();

在服務(wù)端注冊時返回接口可缚,如同binderService獲取到Ibinder一樣。

case TRANSACTION_registerReceiver: {
   data.enforceInterface(DESCRIPTOR);
   com.yglx.learnservice.MessageReceiver _arg0;
   _arg0 = com.yglx.learnservice.MessageReceiver.Stub.asInterface(data.readStrongBinder());
   this.registerReceiver(_arg0);
   reply.writeNoException();
   return true;
 }

總結(jié)

好了一開始提出的三個問題也都解決了斋枢。本文主要也是在應(yīng)用層角度對android進程間通信通過service的一些記錄帘靡,特別在說到binder底層機制上,只用了神秘力量來解釋也是為了不偏離應(yīng)用層這個高度瓤帚。能夠清楚應(yīng)用層的這些情況描姚,就應(yīng)該知道在service時,需要注意一些不要在主線程操作戈次,以及時刻注意處理并發(fā)問題轩勘。
最后附上分析時用的代碼,讀者可以通過拷貝build里aidl生成的接口文件怯邪,然后添加日志的方式绊寻,查看線程的情況,當然筆者代碼也有部分日志輸出代碼悬秉,記得在選中l(wèi)og輸出時對應(yīng)用選擇no-filters澄步。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市和泌,隨后出現(xiàn)的幾起案子驮俗,更是在濱河造成了極大的恐慌,老刑警劉巖允跑,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件王凑,死亡現(xiàn)場離奇詭異搪柑,居然都是意外死亡,警方通過查閱死者的電腦和手機索烹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門工碾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人百姓,你說我怎么就攤上這事渊额。” “怎么了垒拢?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵旬迹,是天一觀的道長。 經(jīng)常有香客問我求类,道長奔垦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任尸疆,我火速辦了婚禮椿猎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寿弱。我一直安慰自己犯眠,他們只是感情好,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布症革。 她就那樣靜靜地躺著筐咧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪噪矛。 梳的紋絲不亂的頭發(fā)上嗜浮,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音摩疑,去河邊找鬼危融。 笑死,一個胖子當著我的面吹牛雷袋,可吹牛的內(nèi)容都是我干的吉殃。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼楷怒,長吁一口氣:“原來是場噩夢啊……” “哼蛋勺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鸠删,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤抱完,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后刃泡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巧娱,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡碉怔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了禁添。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撮胧。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖老翘,靈堂內(nèi)的尸體忽然破棺而出芹啥,到底是詐尸還是另有隱情,我是刑警寧澤铺峭,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布墓怀,位于F島的核電站,受9級特大地震影響卫键,放射性物質(zhì)發(fā)生泄漏傀履。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一永罚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卧秘,春花似錦呢袱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蚯涮,卻和暖如春治专,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背遭顶。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工张峰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人棒旗。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓喘批,卻偏偏與公主長得像,于是被迫代替她去往敵國和親铣揉。 傳聞我的和親對象是個殘疾皇子饶深,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

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

  • 一、IPC簡介 (1)IPC是Inter-Process Communication的縮寫逛拱,含義為進程間通信或者跨...
    遙遙的遠方閱讀 7,200評論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理敌厘,服務(wù)發(fā)現(xiàn),斷路器朽合,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • 3.5 Android進程間通信 3.5.1 背景知識 傳統(tǒng)IPC Linux傳統(tǒng)的IPC機制分為如下幾種:管道俱两、...
    jianhuih閱讀 5,517評論 1 5
  • Binder承擔了絕大部分Android進程通信的職責锋华,可以看做是Android的血管系統(tǒng)嗡官,負責不同服務(wù)模塊進程間...
    看書的小蝸牛閱讀 59,364評論 31 356
  • 1.升級Ruby環(huán)境 2.更換鏡像 更新鏡像文件 查看鏡像文件 3、安裝CocoaPods 鍵入以下指令 如果不行...
    風的低語閱讀 3,910評論 0 0