AIDL簡單使用
來源:程序員人生 發布時間:2016-11-20 16:41:28 閱讀次數:3317次
AIDL是Android Interface Definition Language, 顧名思義,它主要就是用來定義接口的1種語言。Android提供AIDL主要用來進程間通訊。
從AIDL的功能來看,它主要的利用場景就是IPC。雖然同1個進程中的client-service也能夠通過AIDL定義接口來進行通訊,但這并沒有發揮AIDL的主要功能。 概括來講:
- 如果不需要IPC,那就直接實現通過繼承Binder類來實現客戶端和服務端之間的通訊。
- 如果確切需要IPC,但是無需處理多線程,那末就應當通過Messenger來實現。Messenger保證了消息是串行處理的,其內部其實也是通過AIDL來實現。
- 在有IPC需求,同時服務端需要并發處理多個要求的時候,使用AIDL才是必要的
AIDL的簡單使用步驟以下:
- 編寫.AIDL文件,定義需要的接口
- 實現定義的接口
- 將接口暴露給客戶端調用
下面在AS上創建1個工程來使用1下:
創建Aidl文件:

創建完后,可以看到aidl接口文件里面已為我們提供了案例:
// IMyAidlInterface.aidl
package com.example.aidltest;
// Declare any non-default types here with import statements
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);
}
這里先說下AIDL支持以下所述的數據類型:
- 所有的基本類型(int、float等)
- String
- CharSequence
- List
- Map
如果要使用自定義的類型,必須實現Parcelable
接口才能進行進程間通訊。
下面自定義HelloMsg類:
public class HelloMsg implements Parcelable {
private String name;
private int age;
public HelloMsg(String name, int age) {
this.name = name;
this.age = age;
}
protected HelloMsg(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator<HelloMsg> CREATOR = new Creator<HelloMsg>() {
@Override
public HelloMsg createFromParcel(Parcel in) {
return new HelloMsg(in);
}
@Override
public HelloMsg[] newArray(int size) {
return new HelloMsg[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
@Override
public String toString() {
return "HelloMsg{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
定義好HelloMsg.java
以后,還需要新增1個與其對應的AIDL文件。那末一樣依照剛才的步驟右鍵src文件夾,添加1個名為IHelloMsgInterface的AIDL文件。
// IHelloMsgInterface.aidl
package com.example.aidltest;
parcelable HelloMsg;
注意到parcelable
的首字母是小寫的,這算是AIDL1個特殊的地方。
接下來還需要修改IMyAidlInterface.aidl文件,以下:
// IMyAidlInterface.aidl
package com.example.aidltest;
import com.example.aidltest.HelloMsg;
interface IMyAidlInterface {
HelloMsg sayHello();
}
即使IMyAidlInterface.aidl
和IHelloMsgInterface.aidl
位于同1個包下,這里的import
是必須要有的。這也是AIDL1個特殊的地方。
注意:build以后發現會報錯,將IHelloMsgInterfece.aidl重命名為HelloMsg.aidl便可。
build成功以后會在build/generated/souce/aidl/debug目錄下生成IMyAidlInterface文件??梢源笾率炝曄略撐募膬热?,對掌握android binder機制很有幫助
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.aidltest.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.aidltest.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.example.aidltest.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.aidltest.IMyAidlInterface))) {
return ((com.example.aidltest.IMyAidlInterface)iin);
}
return new com.example.aidltest.IMyAidlInterface.Stub.Proxy(obj);
}
這里有個內部類Stub,它繼承系統Binder類和實現IMyAidlInterface接口。另外還提供了asInterface()接口,這個方法接受1個遠端Binder
對象,并將其轉化成Stub
對應的接口對象并返回。在構造方法調用Binder中的attachInterface方法把當前服務對象和描寫符進行關聯。在asInterface方法中會調用queryLocalInterface查詢,如果不在同1進程就返回null,這個時候就返回Proxy對象。
上面看完了Stub類以后,發現他實際上是遠端服務Binder對象的1個中間者,用來和客戶端進行交互的,下面再來看1下Proxy類:
private static class Proxy implements com.example.aidltest.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);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public com.example.aidltest.HelloMsg sayHello() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.aidltest.HelloMsg _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.example.aidltest.HelloMsg.CREATOR.createFromParcel(_reply);
}
可以看到里面有個mRemote對象,它是服務端傳遞過來的binder對象。調用transact方法后會調用上面Stub中的onTransact方法。這里其實用了靜態代理模式,Proxy就是遠端傳遞過來的binder的本地代理??梢岳斫鉃榭蛻粽鎮€中間者。
Stub類是服務真個中間者,1般是實現了AIDL接口類型和繼承了Binder類,具有將Binder對象轉化成原生對象的能力
Proxy類是客戶真個中間者,1般是實現了AIDL接口類型
下面實現服務真個接口,定義RemoteService.java
public class RemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new IMyAidlInterface.Stub() {
@Override
public HelloMsg sayHello() throws RemoteException {
return new HelloMsg("wuliqing", 28);
}
};
}
}
客戶端調用服務端接口代碼以下:
public class MainActivity extends AppCompatActivity {
private IMyAidlInterface iMyAidlInterface = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, RemoteService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(serviceConnection);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
public void onClickToSayHello(View view) {
if (iMyAidlInterface != null) {
try {
HelloMsg helloMsg = iMyAidlInterface.sayHello();
Toast.makeText(this, helloMsg.toString(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show();
}
}
}
}
在onServiceConnected()
回調中,我們使用IMyAidlInterface.Stub.asInterface(service)
方法返回我們的接口的援用。接著客戶端就能夠通過它來對服務端發送要求了。
在這里我為RemoteService設置了process屬性,讓它運行在與默許進程不同的進程中。
<service
android:name="RemoteService"
android:process=":remote" />

從上圖可看出客戶端和服務運行在兩個進程當中。
然后點擊按鈕,成功返回結果。
最后給出1張流程圖,加深印象:

關于AIDL和binder機制可參考下面文章:
Android中AIDL的基本用法
Binder機制和遠程服務調用機制分析
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈