前言
轉載請聲明,轉自【https://www.cnblogs.com/andy-songwei/p/10256379.html】,謝謝!
隻要是面試進階工程師崗位,Android跨程序通信就是最受面試官青睐的知識點之一。Android系統的運作由大量互相獨立的程序互相協助來完成的,是以Android程序間通信問題,是做好Android開發進階工程師必須要跨過的一道坎。但是,我們是否真的清楚,Android中都有哪些方式實作跨程序通信呢?這些方式都有哪些優缺點?如何選擇這些通信方式?Binder是什麼?為什麼要引入Binder?Binder是這麼樣實作跨程序通信的?AIDL是什麼?AIDL和Binder又有什麼關系呢?......
本文将對Android的跨程序通、程序内通信等方面做一些總結,以及比較詳細地介紹AIDL的使用,主要内容如下:
其行文脈絡大緻如下,希望能加深讀者對這方面内容的記憶:(1)Android基于Linux系統,是以先說系統程序相關知識和Linux IPC。(2)總結Android的IPC,順帶總結了Android程序内元件之間的通信方式。(3)Android為了克服Linux IPC中的缺點,引入了Binder,是以對Binder做了一些宏觀上的介紹。(4)AIDL是實作Binder最常用的工具,是以詳細介紹了AIDL相關内容。
一、基礎知識簡介
在介紹Android跨程序通信之前,筆者先簡單啰嗦一下程序隔離、跨程序通信。
1、程序隔離
在作業系統中,程序與程序間的記憶體和資料都是不共享的。兩個程序就好像大海中互相獨立的兩個島嶼,各自生活在互相平行的兩個世界中,互不幹擾,各自為政。這樣做的目的,是為了避免程序間互相操作資料的現象發生,進而引起各自的安全問題。為了實作程序隔離,采用了虛拟位址空間,兩個程序各自的虛拟位址不同,從邏輯上來實作彼此間的隔離。
2、跨程序通信
馬克思主義哲學說,人是一切社會關系的總和。任何一個個體都不可能完全隔離于外界,都不可避免地與外界“互通有無”。程序也一樣,每一個程序完成的功能有限,就像現在的生成線一樣,往往就是隻完成某一類功能,而不是把所有事情都給做了,就這樣,每個程序就時不時需要與其他程序之間通信了。兩個程序之間要進行通信,就需要采用特殊的通信機制:程序間通信(IPC:Inter-Process Communication,即程序間通信或跨程序通信,後文以IPC替代,在此聲明)。
二、Linux跨程序通信
我們知道,Android系統就是基于Linux核心實作的,咱們先簡單了解一下Linux系統的IPC方式。雖然不同的資料對各種方式的名稱和種類說法不完全相同,但是主要來說有如下6種方式:(1)管道 Pipe;(2)信号Signal;(3)信号量Semaphore;(4)消息隊列Message Queue;(5)共享記憶體Shared Memmory;(6)套接字Socket。讀者若想深入了解這些方式,可以自行查閱,這裡不展開介紹,隻需要知道有這六種方式即可。
三、Android跨程序通信
Android IPC的方式在不同的資料中有不同的說法,但從大方向上看,可以歸納為如下四種(這裡僅對各種方式做簡單介紹和優劣對比,對具體如何使用,不做講解):
1、Activity方式
Activity是四大元件中使用最頻繁的,咱們先從它說起。使用Activity方式實作,就是使用startActivity()來啟動另外一個程序的Activity。
(1)場景
我們在使用App的使用,往往會遇到如下幾種情形:(1)浏覽器中看到一篇比較不錯的文章,分享到微信朋友圈或者微網誌;(2)在某個App中點選某個網址,然後界面跳轉到浏覽器中進行閱讀;(3)使用美團外賣app,看到店家的電話,點選聯系商家時,跳轉到了電話撥打界面......這樣的操作再頻繁不過了。這些就是通過startActivity的方式從一個App,跳轉到了另外一個App的Activity,進而實作了跨程序通信。
(2)使用
我們知道,在調用startActivity(Intent intent)的時候,intent有兩個類型:顯式Intent和隐式Intent。
1)顯式Intent的使用方式如下,用于程序内元件間通信:
1 Intent intent = new Intent(this,OtherActivity.class);
2 startActivity(intent);
這種方式顯式地指定了要跳轉的Activtiy的class名稱,不知道是不是因為這個原因而被稱為顯式intent的,筆者沒有查證。這種方式用于程序内Activity的跳轉,是跨子產品間通信,而不是跨程序間通信。
2)隐式intent的使用方式如下,用于IPC:
1 Intent intent = new Intent();
2 intent.setAction(Intent.ACTION_CALL);
3 startActivity(intent);//startActivityForResult()同樣,這裡不贅述
Intent.ACTION_CALL就是字元串常量“android.intent.action.CALL”,這種方式通過setAction的方式來啟動目标app的Activity,上述代碼就是啟動電話app的撥号界面,有時候還可以帶上電話号碼等參數。
由上可知,Activity實作跨程序通信的方式,适合于不同App之間功能界面的跳轉。
2、Content provider(後面簡稱CP)方式
(1)場景
當我們開發App需要用到聯系人,多媒體資訊等資料的時候,往往會通過系統提供Uri,采用CP的方式去擷取。Android系統中,資料主要存儲在自帶的SqlLite資料庫中。應用要共享SqlLite中的資料給其他App操作(增、删、改、查),就要用到CP,也就是說,CP主要用于跨程序資料庫共享。Android系統提供了很多的CP來供其它App使用,如多媒體資訊、聯系人、月曆等。如下圖顯示了Android系統提供的CP,包名都是以"com.android.providers“開頭的:
這些用于共享的資料其實都是存儲在系統資料庫中的,如下顯示了meida CP中的資料庫:
App開發者也可以自定義CP,把自己的資料提供給其它app使用,也可以自己定義操作權限,如隻允許其它app讀取自己的資料,而不允許修改等。
(2)特點
1)CP的使用場景,是提供資料共享。
2)CP本質上還是在操作資料庫,資料存儲在sdcard中,是以建立連接配接和操作資料都是耗時操作,是以注意開辟子線程去操作。
3)當資料庫中資料有變化時,Content Observer監聽到資料庫變化也是有一定的滞後。
3、Broadcase方式
Broadcast使用非常簡單,注冊好廣播,添加上action,就可以等着接收其他程序發出的廣播。發送和接收廣播時,還可以借助Intent來攜帶資料。但是廣播的使用存在很多問題,被很多程式員吐槽,甚至鄙夷,是以選擇用廣播進行跨程序通信,是下下策。下面盤點一下Broadcast的槽點:
(1)Broadcast是一種單向的通信方式。當一個程式發送廣播後,其他應用隻能被動地接收,無法向發送者回報。
(2)Broadcast非常消耗系統資源,會導緻系統性能下降。
(3)速度慢,容易造成系統ANR。且除了Parall Broadcast外,無法保證接收到的時間,甚至不一定能收得到。
(4)如果使用Ordered Broadcast,一個Receiver執行時間過長,會影響後面接收者的接收時間,甚至還有可能被中間某個Receiver攔截,導緻後面Receiver無法接收到。
(5)發送者無法确定誰會接收該廣播,而接收者也無發确認是誰發來的廣播。
(6)如果是靜态注冊的廣播,一個沒有開啟的程序,都有可能被該廣播激活。
......
總而言之,言而總之,使用Broadcast來實作跨程序通信,是下下之策!
4、Service方式
啟動Service的方式有多種,有的用于跨程序通信,有的用于程序内部子產品之間的通信,下面僅簡單介紹一下跨程序通信的方式。
(1)startService()方式
1 Intent startIntent = new Intent ();
2 ComponentName componentName = new ComponentName(string packageName,string serviceClassName);
3 startIntent.setComponent(componentName );
4 startService( startIntent);
該方式啟動遠端Service實作跨程序通信,耦合度比較低,功能及代碼結構清晰,但是存在以下缺點:
1)沒有好的機制立即傳回執行結果,往往Service完成任務後,還需要其他方式向Client端回報。
2)Service端無法識别Client端是誰,隻知道有啟動指令,但無法知道是誰下的指令。
3)在已經建立好Service情況下,每次調用startService,就會執行onStartCommand()生命周期方法,相比于bindService,效率低下。
4)如果Client端忘記調用stopService()了,那麼該Service會一直運作下去,這也是一個隐患。
是以,針對上述缺點,往往建議startService()方式用于同一個App内,跨程序的方式用後面将要講到的AIDL來實作。
(2)bindService、Handler、Messager結合
這種方式也可以實作跨程序通信,據說和AIDL具有相同的功效,但比AIDL使用簡單,詳細可以閱讀博文【Android總結篇系列:Android Service】。但是從筆者工作這些年來看,從沒見過誰使用過這種方式,可能是筆者太孤陋寡聞了,這裡就不多介紹了。
(3)AIDL
這種方式也是bindService()啟動方式的一種使用情況,也是廣受程式員們推崇的方式。前面說startService()和Broadcast如何不好,就是為了襯托AIDL如何的好!這是本文的主角,本文後面會專門詳細講解,這裡不贅述。
值得注意的是,從Android L開始,系統對Service的隐式啟動做了限制,需要至少包含包名或類名,具體可以檢視博文【Android 5.0之後隐式聲明Intent 啟動Service引發的問題】,是以隐式啟動Service時需要多注意這一點。
5、總結
從如上的介紹來看,其實Android中跨程序通信的實作,就是利用四大元件來實作的。對方式的選擇,我們總結一下:
(1)如果跨程序需要界面上的互動操作,用隐式startActivity()方式實作。
(2)如果需要共享資料,用Content Provider方式實作。
(3)排除前兩種情形,就用AIDL。
(4)僅僅為了完成功能,又确實不會用AIDL的,就用Broadcast吧!!!雖然很low,但比實作不了功能還是強多了
四、Android程序内通信
前面總結了幾種跨程序通信的方式,這裡順便總結一下Android程序内部元件之間的通信方式。
(1)顯示調用startActivity()。可以用Intent攜帶資料,而且如果用startActivityForResult(),還可以得到目标Activity完成任務後的回報。
(2)startService/bindService。用于與service通信,其中Intent,回調方法等會攜帶資料或者對象,選擇哪一個需要視情況而定。
(3)Boradcast。對于全局廣播,前面已經diss過很多了,這裡不多說了,能不用則不用。但是還有一種局部廣播——LocalBroadcastManager,隻能在app内部使用,也是一種程序内通信的方法,且在性能、安全等方面都要優于全局廣播,對于它的使用,可以參考我的另外一篇文章【【朝花夕拾】四大元件之(一)廣播篇】。
(4)Handler方式。這種方式在子線程和主線程通信中用得很普遍,跨子產品使用也常見到,使用友善。需要注意的是Looper和線程問題。
(5)回調。最普遍的方式了,使用也很友善。一般會結合面向接口程式設計來實作,如果調用的層次比較深,同一個接口注冊的地方太多,可讀性會比較差,經常看得頭暈。甚至有時候這些注冊回調的地方會互相幹擾。使用的時候要注意退出該子產品的時候,把注冊的回調變量置空銷毀。
(6)EventBus。使用簡單,耦合度低,代碼可讀性比較好,也受到很多開發者的喜愛。但也有一些缺點:1)需要導入第三方庫。2)發送消息出去後,被注冊的地方都會收到該消息,如果處理不當,開發者甚至不容易意識到哪些地方收到了消息并做了處理,帶來不必要的混亂。3)當注冊了EventBus的子產品退出後,容易忘記反注冊。
(7)SharePreference(簡稱SP)等資料存儲方式。這個就是資料的持久化和子產品間共享資料了,除了SP,還有網絡存儲,Sqilite資料庫,檔案存儲等多種方式,功能可以類比跨程序通信的ContentProvider。
和跨程序通行方式選擇一樣,元件間的通信方式也要根據具體需要來确定了。從完成功能上講,前面講到的跨程序通信的方式,在程序内元件間通信也可以用,但是這樣就像用牛刀殺雞,大炮打蚊子了。出于性能方面的考慮,就不要将跨程序的方式用于程序内子產品間通信了。
五、Binder概述
Binder是Android架構中非常重要的一個機制,在Framework中被廣泛使用,了解Binder對閱讀Framework源碼有着很大的幫助。當然,在應用層等層面的跨程序通信中,也被廣泛使用。有人說,了解Binder,是跨入進階Android工程師行列的第一步,先不管是不是誇大其詞了,但足以說明Binder對于Android有多麼重要,也說明了解Binder有一定的難度。
1、Binder是什麼
Binder英文單詞的意思就是“粘合劑”,把兩個物體粘合在一起。說起Binder是什麼,在IPC過程中,從不同的角度來看,它可以被定義為一種機制,或者實體類,或者遠端代理,甚至是傳輸對象,咱們這裡說的Binder是從宏觀定義來看的,是說的Binder機制。在Android中,它是一種高效的IPC方式,是Android所特有的機制。它采用C/S架構模式,基于記憶體映射(mmap()),在系統核心空間将Client端和Service端兩個使用者空間的程序聯系在一起。
2、Binder的由來
Binder前身是某公司開發的OpenBinder項目,後來該項目的作者加入了Google,同時把該項目帶進了Android。從此以後,Binder就成為了Android中主要的跨程序通信機制。
3、Android為什麼要引入Binder
前面第二點提到,Linux已經具備了這麼多的IPC方式,為什麼Android中還要引入Binder呢?原因是從性能、穩定性和安全性三方面考慮,Linux本身的那些方式,往往隻能具備一部分因素,無法兼顧。但是Binder卻不然,從性能上看,隻需要一次資料拷貝,性能上僅次于共享記憶體;從穩定性上看,基于C/S架構,職責明确、架構清晰,穩定性好;從安全性上看,它為每個APP配置設定UID,而程序的UID是鑒别程序身份的重要标志。對于更詳細的對比,推薦閱讀【Android Bander設計與實作 - 設計篇的“引言”部分】,本文重點不是探索Linux,就不贅述了,咱們隻需要知道Binder比Linux其它IPC方式更給力就夠了。
4、Binder機制原理
Binder機制相關知識點和源碼非常龐雜,很多技術書籍往往要花很大篇幅來闡述,比如羅升陽的《Android 系統源碼情景分析》就花了100多頁來剖析它。俗話說,要想倒出一碗水,你得先有一桶水。很不好意思,筆者現在一碗水都不到,這裡就不獻醜了,推薦一篇講得比較好的博文:【寫給 Android 應用工程師的 Binder 原理剖析】,看完後收獲挺大的。
六、AIDL基本使用
前面我們講到,為了克服Linux中IPC各種方式的缺點,在Android中引入了Binder機制。但是當說起Binder在Android中的使用時,幾乎所有的資料都是在說AIDL的使用。AIDL的全稱是Android Interface Definition Language,即Android接口定義語言,是Binder機制實作Android IPC時使用比較廣泛的工具。本節将以一個Demo示範一下AIDL的基本實作步驟。
源碼位址:https://pan.baidu.com/s/1CyE_8-T9TDQLVQ1TDAEX2A 提取碼:4auk 。
如下兩個圖展示了該Demo的結構圖和AIDL關鍵檔案:
圖6.1 圖6.2
1、建立兩個App,分别為Client端和Server端。
這個比較 好了解,Server端就是包含了Service真正幹活的那一端;Client端就是通過遠端操控指揮的那一端,分别在不同的App中。如下圖所示:
2、在Server端main目錄下建立aidl檔案夾以及.aidl檔案,并copy一份到Client端,如圖6.1中②處所示結構。注意,Client端和Server端②處是一模一樣的。另外,AS中提供了快捷方式建立aidl檔案,在main處點選右鍵 > New > AIDL > AIDL File檔案,按照提示給aidl檔案命名即可自動建立完成,可以看到檔案路徑也是該項目的包名。
這裡給aidl命名為IDemoService.aidl,這裡需要注意的是命名規範,一般都是以“I”開頭,表示是一個接口,其内容如下:
複制代碼
1 //========== IDemoService.aidl========
2 package com.songwei.aidldemoserver;
3 // Declare any non-default types here with import statements
4 interface IDemoService {
5 void setName(String name);
6 String getName();
7 }
複制代碼
3、Server端建立Service檔案 AidlService.java,如圖6.1中③處所示,代碼如下:
View Code
為了下文分析流程及生命周期,在其中各個方法中都添加了Log。
同時,在Server端的AndroidManifest.xml檔案中添加該Service的注冊資訊。
複制代碼
1 <service
2 android:name=".AidlService"
3 android:exported=“true”>
4
5
6
7
複制代碼
這裡有幾點需要注意:
(1)exported屬性值,如果有“intent-filter”,則預設值為true,否則為false。是以這裡其實可以去掉,因為有“intent-filter”,其預設值就是true。
(2)由于筆者在後面啟動該service的時候用的action的方式,是以這裡就有了“intent-filter”裡面的action。如果用其他方式啟動,這個service的注冊資訊就需要相應的改動了,有一定開發經驗的讀者應該都知道,就不展開講了,主要是怕讀者容易忽略這裡,是以特别提醒一下。
4、編譯Sever端和Client端App,生成IDemoService.java檔案。
當編譯的時候,AS會自動為我們生成IDemoService.java檔案,如圖6.1和圖6.2中④處所示。當你打開該檔案的時候,是不是看到了如下場景?
驚不驚喜?意不意外?是不是一臉懵逼,大驚,卧槽,這尼瑪啥玩意啊?
AIDL是Android接口定義語言,IDemoService.java是一個java中的interface(接口),現在是不是若有所思了呢?AIDL正是定義了IDemoService.java這個接口!!! 這個接口檔案就是AIDL幫助咱們生成的Binder相關代碼,這些代碼就是用來幫助實作Client端和Server端通信的。前面第2步中提到的IDemoService.aidl檔案,其作用就是作為原料通過AIDL來生成這些你貌似看不懂的代碼的,第3步中的AidlService.java和後續在Client端App連接配接Server端App的時候,其實這個aidl檔案就從來沒有出現過,也就是說,它已經沒有什麼價值了。是以說,AIDL的作用就是用來自動生成Binder相關接口代碼的,而不需要開發者手動編寫。有些教程中說,可以不使用AIDL而手動編寫這份Binder代碼,AIDL不是Binder實作通信所必需的,筆者也沒有嘗試過手動編寫,如果讀者您想挑戰,可以嘗試一下!
咱們繼續!
打開IDemoService.java檔案後,點選主菜單蘭Code > Reformat Code (或 Ctrl + Alt +L快捷健),你會發現畫面變成了下面這個樣子:
View Code
驚不驚喜?意不意外?這下是不是有種似曾相識的趕腳?這就是一個很普通的java中的接口檔案而已,結構也非常簡單。
神秘的面紗揭開一層了吧!後面在講完Client端和Server端的連接配接及通信後,還會繼續深入剖析這個檔案。
5、Client端ClientActivity連接配接Server端AidlService并通信
ClientActivity.java的内容如下,布局檔案在此省略,比較簡單,就兩個按鈕,一個用于綁定,一個用于解綁,看Button命名也很容易分辨。
View Code
代碼中對一些關鍵和容易忽略的地方做了注釋,可以結合起來進行了解。
6、運作
運作的時候,需要先啟動Service端程序,才能在Client端中點選“綁定”的時候綁定成功。完成一次“綁定”和“解綁”,得到的log如下所示:
複制代碼
1 01-08 15:29:43.109 13532-13532/com.songwei.aidldemoserver I/aidlDemo: server:[onCreate]
2 01-08 15:29:43.110 13532-13532/com.songwei.aidldemoserver I/aidlDemo: server:[onBind]
3 01-08 15:29:43.113 13299-13299/com.songwei.aidldemoclient I/aidlDemo: client:[onServiceConnected]componentName=ComponentInfo{com.songwei.aidldemoserver/com.songwei.aidldemoserver.AidlService}
4 01-08 15:29:43.114 13532-13547/com.songwei.aidldemoserver I/aidlDemo: server:[setName]
5 01-08 15:29:43.114 13532-13546/com.songwei.aidldemoserver I/aidlDemo: server:[getName]
6 01-08 15:29:43.114 13299-13299/com.songwei.aidldemoclient I/aidlDemo: client:[onServiceConnected]myName=Andy Song
7 01-08 15:36:07.570 13299-13299/com.songwei.aidldemoclient I/aidlDemo: client:[onServiceDisconnected]
複制代碼
可以結合前面的ClientActivity.java和AidlService.java代碼中的添加的log,來了解一下這個流程。當然,最好是能夠按照上面的步驟,親自動手實作一遍,比看10遍更有效果。
本節對介紹了AIDL最基本的知識,出于項目性質的原因,可能有些Android開發人員工作了好幾年都不一定需要完整寫一個AIDL實作兩個App通信的功能。筆者就是這樣,在前幾年的工作中,雖然很早就知道AIDL這個東西,但确實是項目中就沒有寫過這個功能,直到最近兩年。是以即便是讀者您有不少開發經驗了,也可能和筆者一樣是個AIDL的初級開發者,這也是我花這麼長的篇幅寫這個基礎内容的原因。
七、AIDL的深入使用和了解
前面一節講了AIDL最進本的知識,這一節中将會結合更複雜的場景,更深入地介紹AIDL。(這一節的内容會持續補充完整)
1、Client端是如何實作調用Server端方法的
2、AIDL支援的資料類型
3、AIDL資料類序列化問題
4、AIDL回調的使用
當Server端某個操作執行完後,需要通知Client端自己完成了任務,這個時候回調就帶來了很大的便利,和在同一個App中使用回調效果一樣。例如在上一節的例子中,Server完成了setName()這個操作(耗時的異步操作更能展現回調的作用)後,要通知Client端自己完成了任務,可以進行下一步的操作了,就是這樣一個場景。現在在前面AIDL例子基礎上,對回調的使用步驟進行說明。
源碼位址連結: https://pan.baidu.com/s/1eI8chrxYTGqSaIaeMZEOYg 提取碼: 84cd 。
(1)在Server端IDemoService.aidl同一目錄中添加一個新的.aidl接口檔案,我這裡命名為IDemoCallback.aidl,内容如下:
1 // =IDemoCallback.aidl==
2 package com.songwei.aidldemoserver;
3 // Declare any non-default types here with import statements
4 interface IDemoCallback {
5 void testCallback(String msg);
6 }
(2)在IDemoService.aidl中添加注冊/反注冊兩個方法
複制代碼
1 // =IDemoService.aidl
2 package com.songwei.aidldemoserver;
3
4 // Declare any non-default types here with import statements
5 import com.songwei.aidldemoserver.IDemoCallback;
6
7 interface IDemoService {
8
9 void setName(String name);
10
11 String getName();
12
13 void registerCallback(IDemoCallback cb);
14
15 void unregisterCallback(IDemoCallback cb);
16 }
複制代碼
第13行和第15行為新增的方法。将這兩個.aidl檔案同步到Client端,使C/S兩端的aidl檔案完全一樣,均為
最好将兩個app都編譯一遍,這樣後面有些地方可以用代碼補全,而不用手動書寫。當然在AidlService.java中實作接口的時候肯定會報錯的,把新增的方法補上就可以了。
(3)在AidlService.java中添加如下加粗部分的代碼,
複制代碼
1 private RemoteCallbackList mCallbacks = new RemoteCallbackList<>();
2
3 private void callback(String msg) {
4 int N = mCallbacks.beginBroadcast();
5 for (int i = 0; i < N; i++) {
6 try {
7 mCallbacks.getBroadcastItem(i).testCallback(msg);
8 } catch (RemoteException e) {
9 e.printStackTrace();
10 }
11 }
12 mCallbacks.finishBroadcast();
13 }
14
15 class MyBinder extends IDemoService.Stub {
16 private String mName = “”;
17
18 public void setName(String name) throws RemoteException {
19 Log.i(TAG, “server:[setName]”);
20 mName = name;
21 callback("‘Andy song’ is setted");
22 }
23
24 @Override
25 public String getName() throws RemoteException {
26 Log.i(TAG, “server:[getName]”);
27 return mName;
28 }
29
30 @Override
31 public void registerCallback(IDemoCallback cb) throws RemoteException {
32 Log.i(TAG,“server:[registerCallback]”);
33 if(cb != null){
34 mCallbacks.register(cb);
35 }
36 }
37
38 @Override
39 public void unregisterCallback(IDemoCallback cb) throws RemoteException {
40 Log.i(TAG,“server:[unregisterCallback]”);
41 if(cb != null){
42 mCallbacks.unregister(cb);
43 }
44 }
45
46 }
複制代碼
RemoteCallbackList是系統提供的一個用于存儲回調對象的清單,其對象mCallbacks用于存儲注冊的IDemoCallback對象。通過第3行的callback()方法中的内容,我們可以推測它是采用一種類似于Broadcast的方式來實作回調的。
當setName()方法執行完畢後,callback("‘Andy song’ is setted");就會把回調資訊回報給Client中注冊該回調的地方了。
(4)在ClientActivity.java中注冊回調并處理相關邏輯
複制代碼
1 …
2 public void onServiceConnected(ComponentName componentName, IBinder binder) {
3 //Log.i(TAG, “client:[onServiceConnected]componentName=” + componentName);
4 mIsBinded = true;
5 //得到一個遠端Service中的Binder代理,而不是該Binder執行個體
6 mDemoService = IDemoService.Stub.asInterface(binder);
7 try {
8 mDemoService.registerCallback(mDemoCallback);
9 //遠端控制設定name值
10 mDemoService.setName(“Andy Song”);
11 //遠端擷取設定的name值
12 String myName = mDemoService.getName();
13 Log.i(TAG, “client:[onServiceConnected]myName=” + myName);
14
15 } catch (RemoteException e) {
16 e.printStackTrace();
17 }
18 …
19 private IDemoCallback mDemoCallback = new IDemoCallback.Stub() {
20 @Override
21 public void testCallback(String msg) throws RemoteException {
22 Log.i(TAG, “client:[testCallback] msg=” + msg);
23 }
24 };
25 …
26 private void unRegisterCallback() {
27 try {
28 mDemoService.unregisterCallback(mDemoCallback);
29 } catch (RemoteException e) {
30 e.printStackTrace();
31 }
32 }
33 …
複制代碼
在解綁定的地方調用unRegisterCallback()反注冊回調即可,這樣就完成了代碼整個代碼的編寫。這裡需要注意注冊回調的時機,一定要在setName()執行前注冊,否則Client端收不到回調資訊。
(5)運作C/S端,然後“綁定”/“解綁”,就會看到如下log資訊:
複制代碼
1 01-11 14:11:44.714 5903-5903/com.songwei.aidldemoserver I/aidlDemo: server:[onCreate]
2 01-11 14:11:44.715 5903-5903/com.songwei.aidldemoserver I/aidlDemo: server:[onBind]
3 01-11 14:11:44.720 5903-5916/com.songwei.aidldemoserver I/aidlDemo: server:[registerCallback]
4 01-11 14:11:44.720 5903-5916/com.songwei.aidldemoserver I/aidlDemo: server:[setName]
5 01-11 14:11:44.721 6572-6572/com.songwei.aidldemoclient I/aidlDemo: client:[testCallback] msg=‘Andy song’ is setted
6 01-11 14:11:44.722 5903-5916/com.songwei.aidldemoserver I/aidlDemo: server:[getName]
7 01-11 14:11:44.722 6572-6572/com.songwei.aidldemoclient I/aidlDemo: client:[onServiceConnected]myName=Andy Song
8 01-11 14:11:58.589 5903-5916/com.songwei.aidldemoserver I/aidlDemo: server:[unregisterCallback]
9 01-11 14:11:58.616 5903-5903/com.songwei.aidldemoserver I/aidlDemo: server:[onUnbind]
10 01-11 14:11:58.617 5903-5903/com.songwei.aidldemoserver I/aidlDemo: server:[onDestroy]
複制代碼
第5行就是回調資訊,表示回調成功。
結語
本文主要是筆者用來整理跨程序通信的知識點,以及複習近兩年才真正會用的AIDL,是以詳略上是前半部分略,後半部分詳,完全是按照筆者對知識點掌握程度來行文的。讀者在閱讀中,如果有些部分因為寫得太簡略而看得不過瘾,隻能自己去查更詳細的資料了;如果有些部分因為寫得太詳細而嫌啰嗦,完全可以跳着看;如果有些知識點因為筆者經驗和水準問題寫得有誤或者闡述欠妥,請不吝賜教,萬分感激!!!