久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Android實(shí)戰(zhàn)技術(shù):深入理解Android的RPC方式與AIDL

 Elaine個(gè)人小館 2017-02-27

Understanding ADIL

AIDL是一個(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來看RPCIDL 的更多信息,。
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),。
為什么呢?因?yàn)锳IDL生成的Stub和Proxy用的標(biāo)識(shí)DESCRIPTOR加入了package的名字,。而如果自己實(shí)現(xiàn)接口,,可以任意的寫這個(gè)DESCRIPTOR。
2. 接口的名字實(shí)際上無所謂,,更進(jìn)一步,,其實(shí)方法的簽名也可以不完全一致。因?yàn)檫@二個(gè)接口,,一個(gè)是在Client端,,另一個(gè)是在Server端,。它們之間的聯(lián)系是間接的通過Binder對象實(shí)現(xiàn)的,。只要Binder對象的transact和onTransact二個(gè)方法能找到相應(yīng)的接口方法即可。
關(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對象。
這是ServiceConnection.onServiceConnected的調(diào)用棧:
2. Binder.transact()和Binder.onTransact()的細(xì)節(jié),,這也是具體Binder IPC機(jī)制的細(xì)節(jié),。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多