电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

解析Android AIDL的實例與原理

瀏覽:4日期:2022-09-17 16:23:52
目錄一、概述二、創(chuàng)建 .aidl 文件三、生成 .java 文件四、傳輸復雜數(shù)據(jù)五、建立 service六、獲取服務七、分析調(diào)用過程一、概述

簡單來說,AIDL 就是定義一個接口,客戶端(調(diào)用端)通過 bindService 來與遠程服務端建立一個連接,在該連接建立時會將返回一個 IBinder 對象,該對象是服務端 Binder 的 BinderProxy。在建立連接時,客戶端通過 asInterface 函數(shù)將該 BinderProxy 對象包裝成本地的 Proxy,并賦值給Proxy類的 mRemote 字段,本地通過 mRemote 即可調(diào)用遠程方法。

二、創(chuàng)建 .aidl 文件

首先打開 Android Studio,new 一個 AIDL file。具體代碼如下 :

interface IMyAidlInterface { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);}

basicTypes 方法事接口自帶的,不過可以知道,在 aidl 中只能使用這些基本類型參數(shù):int, long, boolean, float,double, String ;

除了basicTypes 方法之外,我們也可以添加自己的方法。因此,可以刪除basicTypes 方法,添加自己的方法。

三、生成 .java 文件

添加完方法之后,選中 .aidl 文件,在彈出的菜單中選擇 Synchronize LocalAIDLS... Service.java,就會會自動幫你生成對應的 java 代碼。

格式化代碼之后,如下所示:

package com.example.databasetest;public interface IMyAidlInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.databasetest.IMyAidlInterface {private static final java.lang.String DESCRIPTOR = 'com.example.databasetest.IMyAidlInterface';/** * Construct the stub at attach it to the interface. */public Stub() { this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.example.databasetest.IMyAidlInterface interface, * generating a proxy if needed. */public static com.example.databasetest.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) {return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.databasetest.IMyAidlInterface))) {return ((com.example.databasetest.IMyAidlInterface) iin); } return new com.example.databasetest.IMyAidlInterface.Stub.Proxy(obj);}@Overridepublic android.os.IBinder asBinder() { return this;}@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) {case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true;}case TRANSACTION_basicTypes: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0 != data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true;} } return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.example.databasetest.IMyAidlInterface { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) {mRemote = remote; } @Override public android.os.IBinder asBinder() {return mRemote; } public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean) ? (1) : (0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString);// 這里是重點,proxy 持有引用,這樣就可以進行數(shù)據(jù)交換,也不會暴露這個對象 mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException();} finally { _reply.recycle(); _data.recycle();} }}static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;}

如果,你需要修改 .aidl 文件,那么修改之后,選擇 build -> make project 即可,會重新生成對應的java文件。

對于生成的這個java 類,很多剛接觸的人會不理解,這里需要解釋下:

IMyAidlInterface :這個是我們自己定義的一個 servier 接口,也就是將你想要有的功能定義在接口中; IBinder:定義了與遠程對象的交互協(xié)議,代表一種跨進程傳輸?shù)哪芰Γ瑢崿F(xiàn)這個接口,就能將這個對象進行跨進程傳遞,但是如果要使用的話,推薦繼承其子類 Binder; Binder:實現(xiàn)了 IBinder 接口,代表的其實就是Binder 本地對象。BinderProxy 類是 Binder 類的一個內(nèi)部類,它代表遠程進程的 Binder 對象的本地代理;這兩個類都繼承自IBinder, 因而都具有跨進程傳輸?shù)哪芰Γ粚嶋H上,在跨越進程的時候,Binder 驅動會自動完成這兩個對象的轉換。 Stub: AIDL 的時候,編譯工具會給我們生成一個名為 Stub 的靜態(tài)內(nèi)部抽象類;這個類繼承了 Binder, 說明它是一個 Binder 本地對象,它實現(xiàn)了 IInterface 接口,表明它具有 Server 承諾給 Client 的能力;Stub 是一個抽象類,具體的 IInterface 的相關實現(xiàn)需要開發(fā)者自己實現(xiàn)。 IInterface:IInterface 代表的就是 Server 進程對象具備什么樣的能力(能提供哪些方法,其實對應的就是 AIDL 文件中定義的接口) proxy:Stub 的靜態(tài)內(nèi)部類,是一個實現(xiàn)了IMyAidlInterface接口,所以他是一個遠程代理對象,可以用于返回給客戶端用。當 client 調(diào)用 proxy的某個方法的時候,會將參數(shù)傳到 proxy 中,在通過其持有的遠程實際對象,將方法名和參數(shù)等都傳給遠程實際對象,然后就會回調(diào)onTransact,對應的方法就會被調(diào)用,以此來實現(xiàn)跨進程調(diào)用。四、傳輸復雜數(shù)據(jù)

如果,需要傳遞復雜數(shù)據(jù),那么就需要實現(xiàn)Parcelable 接口,可序列化:

public class Info implements Parcelable { private String content; public String getContent() {return content; } public void setContent(String content) {this.content = content; } public Info() { } public Info(Parcel in) {content = in.readString(); } public static final Creator<Info> CREATOR = new Creator<Info>() {@Overridepublic Info createFromParcel(Parcel in) { return new Info(in);}@Overridepublic Info[] newArray(int size) { return new Info[size];} }; @Override public int describeContents() {return 0; } @Override public void writeToParcel(Parcel dest, int flags) {dest.writeString(content); } /** * 參數(shù)是一個Parcel,用它來存儲與傳輸數(shù)據(jù) * * @param dest */ public void readFromParcel(Parcel dest) {//注意,此處的讀值順序應當是和writeToParcel()方法中一致的content = dest.readString(); } //方便打印數(shù)據(jù) @Override public String toString() {return 'content : ' + content; }}

與此同時,也要建一個 info.aidl 文件,表明數(shù)據(jù)也是可以傳遞的。

package com.viii.aidlclient;//注意:Info.Info.java的包名應當是一樣的//這個文件的作用是引入了一個序列化對象 Info 供其他的AIDL文件使用//注意parcelable是小寫parcelable Info;

這樣就可以使用 info 對象了。 不用在受前面的基本類型變量所控制。

五、建立 service

接下去,新建一個Service負責接收消息,并在AndroidManifest.xml里面注冊 Service:

public class MyService extends Service { private static final String TAG = 'MyService'; // private MyBinder mMyBinder = new MyBinder(); @Nullable @Override public IBinder onBind(Intent intent) {Log.d(TAG, 'onBind: '); // 應該返回 mBinderreturn null; } @Override public void onCreate() {Log.d(TAG, 'onCreate: ');super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, 'onStartCommand: ');return super.onStartCommand(intent, flags, startId); }// 這里就是服務端的實現(xiàn),繼承了 stub,想要怎么樣的能力,自己去實現(xiàn) private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {@Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {// 具體實現(xiàn)過程} };}

這時候,可以basicTypes 方法添加具體函數(shù)代碼,實現(xiàn)你想要的功能。

當我們在本地獲取到代理后之后,調(diào)用basicTypes 就會觸發(fā)服務端的調(diào)用。

六、獲取服務

接下去在 mainactivity 中進行綁定。

public class MainActivity extends AppCompatActivity { private static final String TAG = 'MainActivity'; private IMyAidlInterface mService; private boolean mIsBound; private AdditionServiceConnection mServiceConnection; @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);doBindService() ; }/** * bind service */ private void doBindService() {mServiceConnection = new AdditionServiceConnection();Intent intent = new Intent(this, MyService.class);bindService(intent, mServiceConnection, BIND_AUTO_CREATE); } /** * unbind service */ private void doUnbindService() {if (mIsBound) { unbindService(mServiceConnection); mServiceConnection = null; mIsBound = false;} } /** * ServiceConection */ class AdditionServiceConnection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) { // 連接的時候獲取本地代理,這樣我們就可以調(diào)用 service 中的方法了。 mService = IMyAidlInterface.Stub.asInterface((IBinder) service); mIsBound = true; try {//設置死亡代理service.linkToDeath(mDeathRecipient, 0); } catch (RemoteException e) {e.printStackTrace(); } Log.d(TAG, 'onServiceConnected: ');}@Overridepublic void onServiceDisconnected(ComponentName name) { mService = null; mIsBound = false; Log.d(TAG, 'onServiceDisconnected: ');} } /** * 監(jiān)聽Binder是否死亡 */ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {@Overridepublic void binderDied() { if (mService == null) {return; } mService.asBinder().unlinkToDeath(mDeathRecipient, 0); mService = null; //重新綁定 doBindService();} }; @Override protected void onStop() {super.onStop();doUnbindService(); }}

將遠程服務的 binder 拿到之后,我們就可以調(diào)用相關方法實現(xiàn)自己的功能呢。

到這里,一個 AIDL 就被我們實現(xiàn)了。

七、分析調(diào)用過程

看看 asInterface 方法,我們在 bind 一個 Service 之后,在 onServiceConnecttion 的回調(diào)里面,就是通過這個方法拿到一個遠程的 service 的,這個方法做了什么呢?

/** * Cast an IBinder object into an com.example.databasetest.IMyAidlInterface interface, * generating a proxy if needed. */public static com.example.databasetest.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) {return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.databasetest.IMyAidlInterface))) {return ((com.example.databasetest.IMyAidlInterface) iin); }  // 實際上,代理對象持有真實對象,同時代理對象會對數(shù)據(jù)進行處理后,再調(diào)用實體對象的方法 return new com.example.databasetest.IMyAidlInterface.Stub.Proxy(obj);}

首先看函數(shù)的參數(shù)IBinder類型的 obj,這個對象是驅動給我們的,如果是 Binder 本地對象,那么它就是 Binder 類型,如果是 Binder 代理對象,那就是BinderProxy類型;它會試著查找 Binder 本地對象,如果找到,說明 Client 和 Server 都在同一個進程,這個參數(shù)直接就是本地對象,直接強制類型轉換然后返回。

如果找不到,說明是遠程對象(處于另外一個進程)那么就需要創(chuàng)建一個 Binder 代理對象,讓這個 Binder 代理實現(xiàn)對于遠程對象的訪問。一般來說,如果是與一個遠程 Service 對象進行通信,那么這里返回的一定是一個 Binder 代理對象,這個 IBinder 參數(shù)的實際上是 BinderProxy;

再看看我們對于 aidl 的basicTypes方法的實現(xiàn);在 Stub 類里面,basicTypes是一個抽象方法,我們需要繼承這個類并實現(xiàn)它;如果 Client 和 Server 在同一個進程,那么直接就是調(diào)用這個方法;那么,如果是遠程調(diào)用,這中間發(fā)生了什么呢?Client 是如何調(diào)用到 Server 的方法的?

對于遠程方法的調(diào)用,是通過 Binder 代理完成的,在這個例子里面就是Proxy類;Proxy對于basicTypes方法的實現(xiàn)如下:

public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(anInt);_data.writeLong(aLong);_data.writeInt(((aBoolean) ? (1) : (0)));_data.writeFloat(aFloat);_data.writeDouble(aDouble);_data.writeString(aString);// 這里是重點,調(diào)用的是實體對象的方法mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);_reply.readException(); } finally {_reply.recycle();_data.recycle(); }}

它首先用 Parcel 把數(shù)據(jù)序列化了,然后調(diào)用了 transact 方法;這個 transact 到底做了什么呢?這個 Proxy 類在 asInterface 方法里面被創(chuàng)建,前面提到過,如果是 Binder 代理那么說明驅動返回的 IBinder 實際是 BinderProxy,因此我們的 Proxy 類里面的 mRemote 實際類型應該是BinderProxy;我們看看 BinderProxy 的 transact 方法:( Binder.java 的內(nèi)部類)

public native boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;

這是一個本地方法;它的實現(xiàn)在 native 層,具體來說在 frameworks/base/core/jni/android_util_Binder.cpp 文件,里面進行了一系列的函數(shù)調(diào)用,調(diào)用鏈實在太長這里就不給出了;要知道的是它最終調(diào)用到了talkWithDriver函數(shù);看這個函數(shù)的名字就知道,通信過程要交給驅動完成了;這個函數(shù)最后通過 ioctl 系統(tǒng)調(diào)用,Client 進程陷入內(nèi)核態(tài),Client 調(diào)用 basicTypes 方法的線程掛起等待返回;驅動完成一系列的操作之后喚醒 Server 進程,調(diào)用了Server進程本地對象的 onTransact 函數(shù)(實際上由 Server 端線程池完成)。我們再看 Binder 本地對象的 onTransact 方法(這里就是 Stub 類里面的此方法):

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) {case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true;}case TRANSACTION_basicTypes: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0 != data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true;} } return super.onTransact(code, data, reply, flags);}

在 Server 進程里面,onTransact 根據(jù)調(diào)用號(每個 AIDL 函數(shù)都有一個編號,在跨進程的時候,不會傳遞函數(shù),而是傳遞編號指明調(diào)用哪個函數(shù))調(diào)用相關函數(shù)。

在這個例子里面,調(diào)用了 Binder 本地對象的 basicTypes 方法;這個方法將結果返回給驅動,驅動喚醒掛起的 Client 進程里面的線程并將結果返回。于是一次跨進程調(diào)用就完成了。

至此,你應該對 AIDL 這種通信方式里面的各個類以及各個角色有了一定的了解;它總是那么一種固定的模式:一個需要跨進程傳遞的對象一定繼承自 IBinder,如果是 Binder 本地對象,那么一定繼承 Binder 實現(xiàn) IInterface,如果是代理對象,那么就實現(xiàn)了 IInterface 并持有了 IBinder 引用;

Proxy 與 Stub 不一樣,雖然他們都既是 Binder 又是 IInterface,不同的是 Stub 采用的是繼承(is 關系),Proxy采用的是組合(has 關系)。他們均實現(xiàn)了所有的 IInterface 函數(shù)。

不同的是,Stub又使用策略模式調(diào)用的是虛函數(shù)(待子類實現(xiàn)),而 Proxy 則使用組合模式。為什么Stub采用繼承而 Proxy 采用組合?事實上,Stub 本身 is 一個 IBinder(Binder),它本身就是一個能跨越進程邊界傳輸?shù)膶ο螅运美^承 IBinder 實現(xiàn) transact 這個函數(shù)從而得到跨越進程的能力(這個能力由驅動賦予)。

Proxy 類使用組合,是因為他不關心自己是什么,它也不需要跨越進程傳輸,它只需要擁有這個能力即可,要擁有這個能力,只需要保留一個對 IBinder 的引用。

以上就是解析Android AIDL的實例與原理的詳細內(nèi)容,更多關于Android AIDL的資料請關注好吧啦網(wǎng)其它相關文章!

標簽: Android
相關文章:
主站蜘蛛池模板: 注塑机-压铸机-塑料注塑机-卧式注塑机-高速注塑机-单缸注塑机厂家-广东联升精密智能装备科技有限公司 | 柴油发电机组_柴油发电机_发电机组价格-江苏凯晨电力设备有限公司 | 风淋室生产厂家报价_传递窗|送风口|臭氧机|FFU-山东盛之源净化设备 | 光栅尺_Magnescale探规_磁栅尺_笔式位移传感器_苏州德美达 | 食品机械专用传感器-落料放大器-低价接近开关-菲德自控技术(天津)有限公司 | 金属抛光机-磁悬浮抛光机-磁力研磨机-磁力清洗机 - 苏州冠古科技 | 对夹式止回阀_对夹式蝶形止回阀_对夹式软密封止回阀_超薄型止回阀_不锈钢底阀-温州上炬阀门科技有限公司 | 大功率金属激光焊接机价格_不锈钢汽车配件|光纤自动激光焊接机设备-东莞市正信激光科技有限公司 定制奶茶纸杯_定制豆浆杯_广东纸杯厂_[绿保佳]一家专业生产纸杯碗的厂家 | 银川美容培训-美睫美甲培训-彩妆纹绣培训-新娘化妆-学化妆-宁夏倍莱妮职业技能培训学校有限公司 临时厕所租赁_玻璃钢厕所租赁_蹲式|坐式厕所出租-北京慧海通 | 防爆电机生产厂家,YBK3电动机,YBX3系列防爆电机,YBX4节防爆电机--河南省南洋防爆电机有限公司 | 胜为光纤光缆_光纤跳线_单模尾纤_光纤收发器_ODF光纤配线架厂家直销_北京睿创胜为科技有限公司 - 北京睿创胜为科技有限公司 | 纯水设备_苏州皙全超纯水设备水处理设备生产厂家 | 信阳网站建设专家-信阳时代网联-【信阳网站建设百度推广优质服务提供商】信阳网站建设|信阳网络公司|信阳网络营销推广 | 武汉印刷厂-不干胶标签印刷厂-武汉不干胶印刷-武汉标签印刷厂-武汉标签制作 - 善进特种标签印刷厂 | 精密模具-双色注塑模具加工-深圳铭洋宇通 | 电磁流量计厂家_涡街流量计厂家_热式气体流量计-青天伟业仪器仪表有限公司 | 淄博不锈钢无缝管,淄博不锈钢管-鑫门物资有限公司 | 沈阳激光机-沈阳喷码机-沈阳光纤激光打标机-沈阳co2激光打标机 | 数显恒温油浴-电砂浴-高温油浴振荡器-常州迈科诺仪器有限公司 | 首页_中夏易经起名网 | 密集架|电动密集架|移动密集架|黑龙江档案密集架-大量现货厂家销售 | 工业风机_环保空调_冷风机_工厂车间厂房通风降温设备旺成服务平台 | 污水处理设备,一体化泵站,一体化净水设备-「梦之洁环保设备厂家」 | 美侍宠物-专注宠物狗及宠物猫训练|喂养|医疗|繁育|品种|价格 | 水冷式工业冷水机组_风冷式工业冷水机_水冷螺杆冷冻机组-深圳市普威机械设备有限公司 | RTO换向阀_VOC高温阀门_加热炉切断阀_双偏心软密封蝶阀_煤气蝶阀_提升阀-湖北霍科德阀门有限公司 | 交流伺服电机|直流伺服|伺服驱动器|伺服电机-深圳市华科星电气有限公司 | 玻璃钢罐_玻璃钢储罐_盐酸罐厂家-河北华盛节能设备有限公司 | 一体化污水处理设备,一体化污水设备厂家-宜兴市福源水处理设备有限公司 | 岸电电源-60HZ变频电源-大功率变频电源-济南诚雅电子科技有限公司 | 户外-组合-幼儿园-不锈钢-儿童-滑滑梯-床-玩具-淘气堡-厂家-价格 | 电位器_轻触开关_USB连接器_广东精密龙电子科技有限公司 | 食安观察网| 无痕胶_可移胶_无痕双面胶带_可移无痕胶厂家-东莞凯峰 | ICP备案查询_APP备案查询_小程序备案查询 - 备案巴巴 | 苏州教学设备-化工教学设备-环境工程教学模型|同科教仪 | 杭州公司变更法人-代理记账收费价格-公司注销代办_杭州福道财务管理咨询有限公司 | 服务器之家 - 专注于服务器技术及软件下载分享 | 全自动在线分板机_铣刀式在线分板机_曲线分板机_PCB分板机-东莞市亿协自动化设备有限公司 | 圆盘鞋底注塑机_连帮鞋底成型注塑机-温州天钢机械有限公司 | 万博士范文网-您身边的范文参考网站Vanbs.com |