Understanding ADILAIDL是一個(gè)接口描述文件,,用于實(shí)現(xiàn)Android平臺(tái)上面的RPC,,aapt在編譯的時(shí)候會(huì)自動(dòng)根據(jù)規(guī)則生成用于IPC的接口和對象,而作為使用者只需要:1.在服務(wù)端Service實(shí)現(xiàn)接口;2. 在客戶端bindService,,onServiceConnected時(shí)獲取接口對象,。這里的接口都是AIDL中描述的接口,其他的細(xì)節(jié)則在由AIDL生成的同名源碼文件中,。揭開面紗可以看一下gen文件夾下生成的與AIDL文件同名的源碼文件,,這個(gè)文件看似很復(fù)雜,通過這個(gè)文件來可以了解AIDL的本質(zhì),,這里面有一個(gè)接口,,里面在的方法就是AIDL文件中所定義的方法;還有一個(gè)Stub,,這個(gè)就是我們要在Service端實(shí)現(xiàn)的基類,;還有一個(gè)Proxy。它們之間的關(guān)系是這們的,。從使用者的角度來瀏覽這個(gè)源碼文件:它的最外層是一個(gè)與AIDL同名的接口,,這里是PrinterInterface,其內(nèi)有一個(gè)接受String的方法print,。Client端使用時(shí)是用PrinterInterface.Stub.asInterface,,可以看到這個(gè)方法會(huì)返回一個(gè)實(shí)現(xiàn)了PrinterInterface接口的對象。另外就是Server端會(huì)讓Service實(shí)現(xiàn)PrinterInterface.Stub,,其實(shí)是實(shí)現(xiàn)PrinterInterface,,因?yàn)镾tub也繼承自PrinterInterface。所以,,貌似的時(shí)序是這樣的:客戶端獲取了一個(gè)實(shí)現(xiàn)了PrinterInterface接口的對象,,而服務(wù)端要實(shí)現(xiàn)此接口。 但是這樣看起來還是有些亂,,我們需要繼續(xù)脫去它的衣服?。ㄌ鞜岚。美^續(xù)脫啊!) 脫去外套因?yàn)橛葾IDL生成的文件無法編譯,,所以我們創(chuàng)建一個(gè)一模一樣的文件來進(jìn)行,,以方便我們對其進(jìn)行編輯和改動(dòng)。我們分別在獲取IBinder對象時(shí),,Stub的相關(guān)方法里和Proxy的相關(guān)方法里加上日志語句,,以跟蹤程序的行為:通過跟蹤調(diào)試可以得到以下結(jié)論: 當(dāng)通訊的雙方在同一個(gè)進(jìn)程中時(shí),onServiceConnected傳回的對象是Service.onBind()所返回的對象,;但如果是跨進(jìn)程時(shí),,則其返回的是一個(gè)BinderProxy對象。所以,,可以看到在AIDL生成的類中會(huì)有這樣的判斷: if (((iin != null) && (iin instanceof MyPrinterInterface))) { Log.e(TAG, 'we have local interface, so we use it'); return ((MyPrinterInterface) iin); } 這實(shí)際上就是判斷此通訊是在同一進(jìn)程中,,還是跨進(jìn)程,,因?yàn)橥贿M(jìn)程傳回的對象是Service.onBind()所返回的對象,而此對象必然實(shí)現(xiàn)了接口(要不然搞毛?。,。K?,如果僅是在同一個(gè)進(jìn)程之中,,不會(huì)走Binder進(jìn)程IPC,而是直接返回Service所提供的對象,,直接調(diào)用其方法,,因此也就不會(huì)有對象必須Parcelable的限制! 也就是說,,當(dāng)在同一個(gè)進(jìn)程中時(shí)AIDL實(shí)際上變成了這樣的: 也就是說它是直接返回了Service.onBind()的對象,,這其實(shí)跟前面提到的第一種方式:直接實(shí)現(xiàn)Binder對象的方式是一樣一樣的,其他的代碼全是多余的,。因此,,如前面建議的,如果僅是在同一個(gè)進(jìn)程中,,就直接使用Binder就好了,,沒有必要?jiǎng)?chuàng)建AIDL文件,。 當(dāng)在不同的進(jìn)程中時(shí),,客戶端Stub.asInterface會(huì)返回一個(gè)Stub.Proxy對象,調(diào)用其上的print方法,。而服務(wù)端僅會(huì)執(zhí)行Stub.onTransact()方法,,然后就調(diào)到Service的print方法了。 當(dāng)跨進(jìn)程的時(shí)候,,就要使用Binder對象的IPC相關(guān)的方法和機(jī)制,。客戶端需要實(shí)現(xiàn)Binder.transact()方法來執(zhí)行遠(yuǎn)程的一個(gè)方法,,這是給客戶端來使用,;而服務(wù)端則需要實(shí)現(xiàn)Binder.onTransact()來響應(yīng)客戶端所請求的方法。對于上層使用者來說,,用transact()把函數(shù)的信息(參數(shù),,標(biāo)識(shí)和開關(guān))發(fā)送出去,剩下的就是Binder的工作了,,內(nèi)部還有大量的細(xì)節(jié),,但是最終會(huì)調(diào)用到服務(wù)端Binder的onTransact()方法,這里識(shí)別出函數(shù)的標(biāo)識(shí),,然后調(diào)用具體的實(shí)現(xiàn),,再傳回返回值,,這樣一個(gè)IPC的函數(shù)調(diào)用就完成了。 當(dāng)跨進(jìn)程時(shí),,僅以下代碼是各自所必須的,,去掉了無關(guān)代碼: Server service: 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: 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: 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: 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(); } } } }} 本質(zhì)--脫去內(nèi)衣其實(shí)AIDL的作用就是對Binder的二個(gè)方法:Binder.transact()和Binder.onTransact()進(jìn)行封裝,以供Client端和Server端進(jìn)行使用,。因?yàn)閷?shí)現(xiàn)transact()和onTransact()方法的方式基本上是相同的,,所以就可以用模板來生成具體的代碼。理論上講只需要為Client端生成transact()相關(guān)代碼,,為服務(wù)端生成onTransact()代碼即可,,但因?yàn)楣ぞ邿o法準(zhǔn)確的確定某一個(gè)應(yīng)用到底是Client端還是Server端,所以它就生成所有的代碼,,放在一個(gè)文件中,。這就是你看到的自動(dòng)生成的文件。還需要注意的一點(diǎn)是Client端的Proxy是組合Binder對象,,調(diào)用其transact()方法,;而服務(wù)端必須繼承Binder對象,覆寫onTransact()方法,。為蝦米呢,?因?yàn)镃lient是主動(dòng)發(fā)起IPC函數(shù)Call,所以它可以直接調(diào)用Binder的方法來進(jìn)行IPC,。而Server是被動(dòng)的,,是要接收進(jìn)來的IPC call,但Service自己無法得知啥時(shí)候Call會(huì)來,,因此必須實(shí)現(xiàn)回調(diào)(onTransact())給Binder,,以讓Binder在有IPC Call進(jìn)來的時(shí)候告訴Service。 原理和內(nèi)幕AIDL的角色是實(shí)現(xiàn)Android平臺(tái)上面的RPC(Remote Procedure Call)也即遠(yuǎn)程例程調(diào)用,。RPC是IPC中的一種,,但是它是以調(diào)用在本地或者另一個(gè)進(jìn)程,甚至是另一個(gè)主機(jī)上的方法的機(jī)制,。RPC的目的就是可以讓程序不用擔(dān)心方法具體是在哪個(gè)進(jìn)程里面或者哪以機(jī)器上面,,就像正常的本地方法那樣去調(diào)用即可,RPC機(jī)制會(huì)處理所有的具體細(xì)節(jié),。RPC一般用IDL(Interface Definition Language)來描述,,實(shí)現(xiàn)則要看具體的平臺(tái)和語言??梢詤⒖糤ikipedia來看RPC 和IDL 的更多信息,。AIDL提供Android平臺(tái)的RPC的支持:開發(fā)者僅需要要定義AIDL,做一些相關(guān)的適配工作,,然后就可以使用這些方法了,,不用具體關(guān)心接口描述的方法空究竟是在同一個(gè)進(jìn)程中還是在其他的進(jìn)程中,。這些RPC實(shí)現(xiàn)的細(xì)節(jié)由Binder和系統(tǒng)來處理。 Binder RPC的流程: 可以看到這個(gè)流程是一個(gè)標(biāo)準(zhǔn)的RPC流程,。 不用AIDL來實(shí)現(xiàn)知道了AIDL的本質(zhì)后,,就可以不用AIDL來實(shí)現(xiàn)IPC,雖然AIDL簡單方便,,但是它卻非常不容易理解,,而且代碼有冗余(服務(wù)端并不需要為Client準(zhǔn)備的對象,反之亦然),。所以我們可以自已實(shí)現(xiàn): Server interface: 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: 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: 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: 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有二個(gè)好處: 1. 自己實(shí)現(xiàn)還有一個(gè)好處就是可以隨意設(shè)計(jì)包名,。如果使用AIDL則Client端的AIDL文件所在package必須與Server端的AIDL的package完全一致,否則會(huì)找不到service,,Client端會(huì)有異常,。但如果自己實(shí)現(xiàn)接口,就沒有此限制,,可以把接口文件放在任何的package內(nèi),。關(guān)鍵的通訊標(biāo)識(shí)和方法標(biāo)識(shí) 從上面的例子可以看出客戶Proxy的transact,,和服務(wù)Stub的onTransact使用二個(gè)標(biāo)識(shí)來識(shí)別對方:一個(gè)是DESCRIPTOR,,這個(gè)是標(biāo)識(shí)Binder的Token,也就是說是標(biāo)識(shí)服務(wù)端和客戶端,;方法的標(biāo)識(shí)就是TRANSACTION_print,,是用來標(biāo)識(shí)調(diào)用的是哪個(gè)方法。這個(gè)理解起來也不是很難,,就好比打電話,,先要通過通訊的標(biāo)識(shí)電話號(hào)碼找到相應(yīng)的人,然后跟人說的時(shí)候要告訴他是哪件事(哪個(gè)方法),。 接下來可以二個(gè)方面來進(jìn)行深入的研究: 1. bindService是如何獲得Binder對象的(無論是本地時(shí)Service的實(shí)現(xiàn),,還是遠(yuǎn)程時(shí)的BinderProxy),,或者說是如何查詢到Binder對象。 |
|