生活随笔
收集整理的這篇文章主要介紹了
Android实战技术:深入理解Android的RPC方式与AIDL
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
目錄(?)[-]
Understanding ADIL揭開面紗脫去外套本質--脫去內衣原理和內幕不用AIDL來實現
Understanding ADIL
AIDL是一個接口描述文件,用于實現Android平臺上面的RPC,aapt在編譯的時候會自動根據規則生成用于IPC的接口和對象,而作為使用者只需要:1.在服務端Service實現接口;2. 在客戶端bindService,onServiceConnected時獲取接口對象。這里的接口都是AIDL中描述的接口,其他的細節則在由AIDL生成的同名源碼文件中。
揭開面紗
可以看一下gen文件夾下生成的與AIDL文件同名的源碼文件,這個文件看似很復雜,通過這個文件來可以了解AIDL的本質,這里面有一個接口,里面在的方法就是AIDL文件中所定義的方法;還有一個Stub,這個就是我們要在Service端實現的基類;還有一個Proxy。它們之間的關系是這們的。
從使用者的角度來瀏覽這個源碼文件:它的最外層是一個與AIDL同名的接口,這里是PrinterInterface,其內有一個接受String的方法print。Client端使用時是用PrinterInterface.Stub.asInterface,可以看到這個方法會返回一個實現了PrinterInterface接口的對象。另外就是Server端會讓Service實現PrinterInterface.Stub,其實是實現PrinterInterface,因為Stub也繼承自PrinterInterface。所以,貌似的時序是這樣的:客戶端獲取了一個實現了PrinterInterface接口的對象,而服務端要實現此接口。
但是這樣看起來還是有些亂,我們需要繼續脫去它的衣服!(天熱啊,得繼續脫啊!)
脫去外套
因為由AIDL生成的文件無法編譯,所以我們創建一個一模一樣的文件來進行,以方便我們對其進行編輯和改動。我們分別在獲取IBinder對象時,Stub的相關方法里和Proxy的相關方法里加上日志語句,以跟蹤程序的行為:
通過跟蹤調試可以得到以下結論:
當通訊的雙方在同一個進程中時,onServiceConnected傳回的對象是Service.onBind()所返回的對象;但如果是跨進程時,則其返回的是一個BinderProxy對象。所以,可以看到在AIDL生成的類中會有這樣的判斷:
[java]?view plain
?copy ?print?
if?(((iin?!=?null)?&&?(iin?instanceof?MyPrinterInterface)))?{?? ????Log.e(TAG,?"we?have?local?interface,?so?we?use?it");?? ????????return?((MyPrinterInterface)?iin);?? }?? 這實際上就是判斷此通訊是在同一進程中,還是跨進程,因為同一進程傳回的對象是Service.onBind()所返回的對象,而此對象必然實現了接口(要不然搞毛啊!)。所以,如果僅是在同一個進程之中,不會走Binder進程IPC,而是直接返回Service所提供的對象,直接調用其方法,因此也就不會有對象必須Parcelable的限制!
也就是說,當在同一個進程中時AIDL實際上變成了這樣的:
也就是說它是直接返回了Service.onBind()的對象,這其實跟前面提到的第一種方式:直接實現Binder對象的方式是一樣一樣的,其他的代碼全是多余的。因此,如前面建議的,如果僅是在同一個進程中,就直接使用Binder就好了,沒有必要創建AIDL文件。
當在不同的進程中時,客戶端Stub.asInterface會返回一個Stub.Proxy對象,調用其上的print方法。而服務端僅會執行Stub.onTransact()方法,然后就調到Service的print方法了。
當跨進程的時候,就要使用Binder對象的IPC相關的方法和機制??蛻舳诵枰獙崿FBinder.transact()方法來執行遠程的一個方法,這是給客戶端來使用;而服務端則需要實現Binder.onTransact()來響應客戶端所請求的方法。對于上層使用者來說,用transact()把函數的信息(參數,標識和開關)發送出去,剩下的就是Binder的工作了,內部還有大量的細節,但是最終會調用到服務端Binder的onTransact()方法,這里識別出函數的標識,然后調用具體的實現,再傳回返回值,這樣一個IPC的函數調用就完成了。
當跨進程時,僅以下代碼是各自所必須的,去掉了無關代碼:
Server service: [java]?view plain
?copy ?print?
public?class?MyServerService?extends?Service?{?? ????private?static?final?String?TAG?=?"MyServerService";?? ????private?Handler?mHandler?=?new?Handler();?? ????@Override?? ????public?IBinder?onBind(Intent?intent)?{?? ????????return?mBinder;?? ????}?? ?????? ????private?MyPrinterInterfaceStub?mBinder?=?new?MyPrinterInterfaceStub()?{?? ????????@Override?? ????????public?void?print(String?msg)?throws?RemoteException?{?? ????????????MyServerService.this.print(msg);?? ????????}?? ????};?? ?????? ????public?void?print(String?msg)?{?? ????????try?{?? ????????????Log.e(TAG,?"Preparing?printer...");?? ????????????Thread.sleep(1000);?? ????????????Log.e(TAG,?"Connecting?printer...");?? ????????????Thread.sleep(1000);?? ????????????Log.e(TAG,?"Printing....?"?+?msg);?? ????????????Thread.sleep(1000);?? ????????????Log.e(TAG,?"Done");?? ????????}?catch?(InterruptedException?e)?{?? ????????}?? ????????mHandler.post(new?Runnable()?{?? ????????????@Override?? ????????????public?void?run()?{?? ????????????????Toast.makeText(MyServerService.this,?"MyServerService?Printing?is?done.",?Toast.LENGTH_LONG).show();?? ????????????}?? ????????});?? ????}?? }?? serer side interface definition:
[java]?view plain
?copy ?print?
public?interface?MyPrinterInterface?extends?android.os.IInterface?{?? ????public?void?print(String?msg)?throws?android.os.RemoteException;?? }?? ?? ?? abstract?class?MyPrinterInterfaceStub?extends?Binder?implements?MyPrinterInterface?{?? ????private?static?final?String?DESCRIPTOR?=?"MyPrinterInterface";?? ????private?static?final?String?TAG?=?"MyPrinterInterfaceStub";?? ?? ?? ????public?MyPrinterInterfaceStub()?{?? ????????attachInterface(this,?DESCRIPTOR);?? ????}?? ?? ?? ????@Override?? ????public?IBinder?asBinder()?{?? ????????return?this;?? ????}?? ?? ?? ????@Override?? ????public?boolean?onTransact(int?code,?Parcel?data,?Parcel?reply,?int?flags)?? ????????????throws?android.os.RemoteException?{?? ????????Log.e(TAG,?"onTransact,?code?is?"?+?code);?? ????????switch?(code)?{?? ????????case?INTERFACE_TRANSACTION:?{?? ????????????Log.e(TAG,?"onTransact,?code?is?"?+?code?+?",?when?this?happens");?? ????????????reply.writeString(DESCRIPTOR);?? ????????????return?true;?? ????????}?? ????????case?TRANSACTION_print:?{?? ????????????data.enforceInterface(DESCRIPTOR);?? ????????????String?_arg0;?? ????????????_arg0?=?data.readString();?? ????????????Log.e(TAG,?"ontransact,?arg?is?"?+?_arg0?+?",?when?this?happened?");?? ????????????this.print(_arg0);?? ????????????reply.writeNoException();?? ????????????return?true;?? ????????}?? ????????}?? ????????return?super.onTransact(code,?data,?reply,?flags);?? ????}?? ?????? ????static?final?int?TRANSACTION_print?=?(IBinder.FIRST_CALL_TRANSACTION?+?0);?? }?? Client activity:
[java]?view plain
?copy ?print?
public?class?AnotherMyClientActivity?extends?Activity?{?? ????private?static?final?String?TAG?=?"MyClientActivity";?? ????MyPrinterInterface?mService;?? ?? ?? ????@Override?? ????protected?void?onCreate(Bundle?savedInstanceState)?{?? ????????super.onCreate(savedInstanceState);?? ????????setContentView(R.layout.printer_activity);?? ????????setTitle("My?interface?another?client?Activity");?? ????????((Button)?findViewById(R.id.play)).setText("Print?via?my?interface");?? ????}?? ?? ?? ????@Override?? ????protected?void?onStart()?{?? ????????super.onStart();?? ????????doBindService();?? ????}?? ?? ?? ????private?void?doBindService()?{?? ????????Intent?intent?=?new?Intent();?? ????????intent.setClassName("com.example.effectiveandroid",?"com.example.effectiveandroid.MyServerService");?? ????????bindService(intent,?mConnection,?Context.BIND_AUTO_CREATE);?? ????}?? ?? ?? ????@Override?? ????protected?void?onStop()?{?? ????????super.onStop();?? ????????doUnbindService();?? ????}?? ?? ?? ????private?void?doUnbindService()?{?? ????????if?(mService?!=?null)?{?? ????????????unbindService(mConnection);?? ????????}?? ????}?? ?????? ????public?void?onButtonClick(View?v)?{?? ????????if?(mService?==?null)?{?? ????????????Log.e(TAG,?"what?the?fucl?service?is?not?ready");?? ????????????return;?? ????????}?? ????????try?{?? ????????????mService.print("In?another?application,?create?a?client?based?on?user?defined?IPC?interfaces");?? ????????}?catch?(RemoteException?e)?{?? ????????????e.printStackTrace();?? ????????}?? ????}?? ?????? ????private?ServiceConnection?mConnection?=?new?ServiceConnection()?{?? ????????@Override?? ????????public?void?onServiceConnected(ComponentName?className,?IBinder?service)?{?? ????????????Log.e(TAG,?"on?service?connected,?service?obj?"?+?service);?? ????????????mService?=?MyPrinterInterface.Stub.asInterface(service);?? ????????}?? ?? ?? ????????@Override?? ????????public?void?onServiceDisconnected(ComponentName?arg0)?{?? ????????????mService?=?null;?? ????????}?? ????};?? }?? client side interface definiition:
[java]?view plain
?copy ?print?
public?interface?MyPrinterInterface?extends?android.os.IInterface?{?? ????public?void?print(String?msg)?throws?android.os.RemoteException;?? ?????? ????public?abstract?class?Stub?extends?Binder?implements?MyPrinterInterface?{?? ????????private?static?final?String?DESCRIPTOR?=?"MyPrinterInterface";?? ????????private?static?final?String?TAG?=?"MyPrinterInterface.Stub";?? ?????????? ????????public?Stub()?{?? ????????????attachInterface(this,?DESCRIPTOR);?? ????????}?? ?????????? ????????public?static?MyPrinterInterface?asInterface(IBinder?obj)?{?? ????????????if?((obj?==?null))?{?? ????????????????return?null;?? ????????????}?? ?? ?? ????????????Log.e(TAG,?"we?are?talking?to?a?remote?one,?we?must?use?a?proxy?object?to?wrapper?binder");?? ????????????return?new?Stub.Proxy(obj);?? ????????}?? ?????????? ????????static?final?int?TRANSACTION_print?=?(IBinder.FIRST_CALL_TRANSACTION?+?0);?? ?????????? ????????private?static?class?Proxy?implements?MyPrinterInterface?{?? ????????????private?IBinder?mRemote;?? ?????????????? ????????????Proxy(IBinder?remote)?{?? ????????????????mRemote?=?remote;?? ????????????}?? ?????????????? ????????????@Override?? ????????????public?IBinder?asBinder()?{?? ????????????????return?mRemote;?? ????????????}?? ?????????????? ????????????public?String?getInterfaceDescriptor()?{?? ????????????????return?DESCRIPTOR;?? ????????????}?? ?????????????? ????????????@Override?? ????????????public?void?print(String?msg)?throws?android.os.RemoteException?{?? ????????????????Parcel?_data?=?Parcel.obtain();?? ????????????????Parcel?_reply?=?Parcel.obtain();?? ????????????????try?{?? ????????????????????_data.writeInterfaceToken(DESCRIPTOR);?? ????????????????????_data.writeString(msg);?? ????????????????????mRemote.transact(Stub.TRANSACTION_print,?_data,?_reply,?0);?? ????????????????????Log.e(TAG,?"lalalala,?let?us?passing?the?parameters?and?calling?the?message");?? ????????????????????_reply.readException();?? ????????????????}?finally?{?? ????????????????????_reply.recycle();?? ????????????????????_data.recycle();?? ????????????????}?? ????????????}?? ????????}?? ????}?? }?? 本質--脫去內衣
其實AIDL的作用就是對Binder的二個方法:Binder.transact()和Binder.onTransact()進行封裝,以供Client端和Server端進行使用。因為實現transact()和onTransact()方法的方式基本上是相同的,所以就可以用模板來生成具體的代碼。理論上講只需要為Client端生成transact()相關代碼,為服務端生成onTransact()代碼即可,但因為工具無法準確的確定某一個應用到底是Client端還是Server端,所以它就生成所有的代碼,放在一個文件中。這就是你看到的自動生成的文件。
還需要注意的一點是Client端的Proxy是組合Binder對象,調用其transact()方法;而服務端必須繼承Binder對象,覆寫onTransact()方法。為蝦米呢?因為Client是主動發起IPC函數Call,所以它可以直接調用Binder的方法來進行IPC。而Server是被動的,是要接收進來的IPC call,但Service自己無法得知啥時候Call會來,因此必須實現回調(onTransact())給Binder,以讓Binder在有IPC Call進來的時候告訴Service。
原理和內幕
AIDL的角色是實現Android平臺上面的RPC(Remote Procedure Call)也即遠程例程調用。RPC是IPC中的一種,但是它是以調用在本地或者另一個進程,甚至是另一個主機上的方法的機制。RPC的目的就是可以讓程序不用擔心方法具體是在哪個進程里面或者哪以機器上面,就像正常的本地方法那樣去調用即可,RPC機制會處理所有的具體細節。RPC一般用IDL(Interface Definition Language)來描述,實現則要看具體的平臺和語言??梢詤⒖糤ikipedia來看RPC?和IDL?的更多信息。
AIDL提供Android平臺的RPC的支持:開發者僅需要要定義AIDL,做一些相關的適配工作,然后就可以使用這些方法了,不用具體關心接口描述的方法空究竟是在同一個進程中還是在其他的進程中。這些RPC實現的細節由Binder和系統來處理。
Binder RPC的流程:<Binder RPC sequence>
可以看到這個流程是一個標準的RPC流程。
不用AIDL來實現
知道了AIDL的本質后,就可以不用AIDL來實現IPC,雖然AIDL簡單方便,但是它卻非常不容易理解,而且代碼有冗余(服務端并不需要為Client準備的對象,反之亦然)。
所以我們可以自已實現:
Server interface:
[java]?view plain
?copy ?print?
public?interface?ServerPrinterInterface?{?? ????public?void?print(String?msg)?throws?android.os.RemoteException;?? }?? ?? ?? abstract?class?MyPrinterInterfaceStub?extends?Binder?implements?ServerPrinterInterface,?IInterface?{?? ????private?static?final?String?DESCRIPTOR?=?"MyPrinterInterface";?? ????static?final?int?TRANSACTION_print?=?(IBinder.FIRST_CALL_TRANSACTION?+?0);?? ????private?static?final?String?TAG?=?"MyPrinterInterfaceStub";?? ?? ?? ????public?MyPrinterInterfaceStub()?{?? ????????attachInterface(this,?DESCRIPTOR);?? ????}?? ?? ?? ????@Override?? ????public?IBinder?asBinder()?{?? ????????return?this;?? ????}?? ?? ?? ????@Override?? ????public?boolean?onTransact(int?code,?Parcel?data,?Parcel?reply,?int?flags)?? ????????????throws?android.os.RemoteException?{?? ????????Log.e(TAG,?"onTransact,?code?is?"?+?code);?? ????????switch?(code)?{?? ????????case?INTERFACE_TRANSACTION:?{?? ????????????Log.e(TAG,?"onTransact,?code?is?"?+?code?+?",?when?this?happens");?? ????????????reply.writeString(DESCRIPTOR);?? ????????????return?true;?? ????????}?? ????????case?TRANSACTION_print:?{?? ????????????data.enforceInterface(DESCRIPTOR);?? ????????????String?_arg0;?? ????????????_arg0?=?data.readString();?? ????????????Log.e(TAG,?"ontransact,?arg?is?"?+?_arg0?+?",?when?this?happened?");?? ????????????this.print(_arg0);?? ????????????reply.writeNoException();?? ????????????return?true;?? ????????}?? ????????}?? ????????return?super.onTransact(code,?data,?reply,?flags);?? ????}?? }?? service:
[java]?view plain
?copy ?print?
public?class?MyServerService?extends?Service?{?? ????private?static?final?String?TAG?=?"MyServerService";?? ????private?Handler?mHandler?=?new?Handler();?? ????@Override?? ????public?IBinder?onBind(Intent?intent)?{?? ????????return?mBinder;?? ????}?? ?????? ????private?MyPrinterInterfaceStub?mBinder?=?new?MyPrinterInterfaceStub()?{?? ????????@Override?? ????????public?void?print(String?msg)?throws?RemoteException?{?? ????????????MyServerService.this.print(msg);?? ????????}?? ????};?? ?????? ????public?void?print(String?msg)?{?? ????????try?{?? ????????????Log.e(TAG,?"Preparing?printer...");?? ????????????Thread.sleep(1000);?? ????????????Log.e(TAG,?"Connecting?printer...");?? ????????????Thread.sleep(1000);?? ????????????Log.e(TAG,?"Printing....?"?+?msg);?? ????????????Thread.sleep(1000);?? ????????????Log.e(TAG,?"Done");?? ????????}?catch?(InterruptedException?e)?{?? ????????}?? ????????mHandler.post(new?Runnable()?{?? ????????????@Override?? ????????????public?void?run()?{?? ????????????????Toast.makeText(MyServerService.this,?"MyServerService?Printing?is?done.",?Toast.LENGTH_LONG).show();?? ????????????}?? ????????});?? ????}?? }?? client interface:
[java]?view plain
?copy ?print?
public?interface?ClientPrinterInterface?{?? ????public?void?print(String?msg)?throws?android.os.RemoteException;?? }?? ?? ?? class?MyPrinterInterfaceProxy?implements?ClientPrinterInterface?{?? ????private?static?final?String?DESCRIPTOR?=?"MyPrinterInterface";?? ????static?final?int?TRANSACTION_print?=?(IBinder.FIRST_CALL_TRANSACTION?+?0);?? ????private?static?final?String?TAG?=?"MyPrinterInterfaceProxy";?? ????private?IBinder?mRemote;?? ?? ?? ????MyPrinterInterfaceProxy(IBinder?remote)?{?? ????????mRemote?=?remote;?? ????}?? ?? ?? ????@Override?? ????public?void?print(String?msg)?throws?android.os.RemoteException?{?? ????????Parcel?_data?=?Parcel.obtain();?? ????????Parcel?_reply?=?Parcel.obtain();?? ????????try?{?? ????????????_data.writeInterfaceToken(DESCRIPTOR);?? ????????????_data.writeString(msg);?? ????????????mRemote.transact(TRANSACTION_print,?_data,?_reply,?0);?? ????????????Log.e(TAG,?"lalalala,?let?us?passing?the?parameters?and?calling?the?message");?? ????????????_reply.readException();?? ????????}?finally?{?? ????????????_reply.recycle();?? ????????????_data.recycle();?? ????????}?? ????}?? }?? client activity:
[java]?view plain
?copy ?print?
public?class?AnotherMyClientActivity?extends?Activity?{?? ????private?static?final?String?TAG?=?"MyClientActivity";?? ????ClientPrinterInterface?mService;?? ?? ?? ????@Override?? ????protected?void?onCreate(Bundle?savedInstanceState)?{?? ????????super.onCreate(savedInstanceState);?? ????????setContentView(R.layout.printer_activity);?? ????????setTitle("My?interface?another?client?Activity");?? ????????((Button)?findViewById(R.id.play)).setText("Print?via?my?interface");?? ????}?? ?? ?? ????@Override?? ????protected?void?onStart()?{?? ????????super.onStart();?? ????????doBindService();?? ????}?? ?? ?? ????private?void?doBindService()?{?? ????????Intent?intent?=?new?Intent();?? ????????intent.setClassName("com.example.effectiveandroid",?"com.example.effectiveandroid.MyServerService");?? ????????bindService(intent,?mConnection,?Context.BIND_AUTO_CREATE);?? ????}?? ?? ?? ????@Override?? ????protected?void?onStop()?{?? ????????super.onStop();?? ????????doUnbindService();?? ????}?? ?? ?? ????private?void?doUnbindService()?{?? ????????if?(mService?!=?null)?{?? ????????????unbindService(mConnection);?? ????????}?? ????}?? ?????? ????public?void?onButtonClick(View?v)?{?? ????????if?(mService?==?null)?{?? ????????????Log.e(TAG,?"what?the?fucl?service?is?not?ready");?? ????????????return;?? ????????}?? ????????try?{?? ????????????mService.print("In?another?application,?create?a?client?based?on?user?defined?IPC?interfaces");?? ????????}?catch?(RemoteException?e)?{?? ????????????e.printStackTrace();?? ????????}?? ????}?? ?????? ????private?ServiceConnection?mConnection?=?new?ServiceConnection()?{?? ????????@Override?? ????????public?void?onServiceConnected(ComponentName?className,?IBinder?service)?{?? ????????????Log.e(TAG,?"on?service?connected,?service?obj?"?+?service);?? ????????????mService?=?new?MyPrinterInterfaceProxy(service);?? ????????}?? ?? ?? ????????@Override?? ????????public?void?onServiceDisconnected(ComponentName?arg0)?{?? ????????????mService?=?null;?? ????????}?? ????};?? }?? 從這里可以看到不使用AIDL有二個好處:
1. 自己實現還有一個好處就是可以隨意設計包名。如果使用AIDL則Client端的AIDL文件所在package必須與Server端的AIDL的package完全一致,否則會找不到service,Client端會有異常。但如果自己實現接口,就沒有此限制,可以把接口文件放在任何的package內。
為什么呢?因為AIDL生成的Stub和Proxy用的標識DESCRIPTOR加入了package的名字。而如果自己實現接口,可以任意的寫這個DESCRIPTOR。
2. 接口的名字實際上無所謂,更進一步,其實方法的簽名也可以不完全一致。因為這二個接口,一個是在Client端,另一個是在Server端。它們之間的聯系是間接的通過Binder對象實現的。只要Binder對象的transact和onTransact二個方法能找到相應的接口方法即可。 關鍵的通訊標識和方法標識
從上面的例子可以看出客戶Proxy的transact,和服務Stub的onTransact使用二個標識來識別對方:一個是DESCRIPTOR,這個是標識Binder的Token,也就是說是標識服務端和客戶端;方法的標識就是TRANSACTION_print,是用來標識調用的是哪個方法。這個理解起來也不是很難,就好比打電話,先要通過通訊的標識電話號碼找到相應的人,然后跟人說的時候要告訴他是哪件事(哪個方法)。
接下來可以二個方面來進行深入的研究:
1. bindService是如何獲得Binder對象的(無論是本地時Service的實現,還是遠程時的BinderProxy),或者說是如何查詢到Binder對象。
這是ServiceConnection.onServiceConnected的調用棧:
2. Binder.transact()和Binder.onTransact()的細節,這也是具體Binder IPC機制的細節。
原文地址:?http://blog.csdn.net/hitlion2008/article/details/9824009
總結
以上是生活随笔為你收集整理的Android实战技术:深入理解Android的RPC方式与AIDL的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。