Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> AIDL簡單使用

AIDL簡單使用

編輯:關於Android編程

AIDL是Android Interface Definition Language, 顧名思義,它主要就是用來定義接口的一種語言。Android提供AIDL主要用來進程間通訊。

AIDL的功能來看,它主要的應用場景就是IPC。雖然同一個進程中的client-service也能夠通過AIDL定義接口來進行通信,但這並沒有發揮AIDL的主要功能。 概括來說:

如果不需要IPC,那就直接實現通過繼承Binder類來實現客戶端和服務端之間的通信。 如果確實需要IPC,但是無需處理多線程,那麼就應該通過Messenger來實現。Messenger保證了消息是串行處理的,其內部其實也是通過AIDL來實現。 在有IPC需求,同時服務端需要並發處理多個請求的時候,使用AIDL才是必要的

AIDL的簡單使用步驟如下:

編寫.AIDL文件,定義需要的接口 實現定義的接口 將接口暴露給客戶端調用

下面在AS上創建一個工程來使用一下:

創建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等)StringCharSequenceListMap

如果要使用自定義的類型,必須實現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 CREATOR = new Creator() {
        @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之後,還需要新增一個與其對應的AIDL文件。那麼同樣按照剛才的步驟右鍵src文件夾,添加一個名為IHelloMsgInterface的AIDL文件。

// IHelloMsgInterface.aidl
package com.example.aidltest;
parcelable HelloMsg;

注意到parcelable的首字母是小寫的,這算是AIDL一個特殊的地方。
接下來還需要修改IMyAidlInterface.aidl文件,如下:

// IMyAidlInterface.aidl
package com.example.aidltest;

import com.example.aidltest.HelloMsg;

interface IMyAidlInterface {
    HelloMsg sayHello();
}


即便IMyAidlInterface.aidl和IHelloMsgInterface.aidl位於同一個包下,這裡的import是必須要有的。這也是AIDL一個特殊的地方。


注意: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()接口,這個方法接受一個遠端Binder對象,並將其轉化成Stub對應的接口對象並返回。在構造方法調用Binder中的attachInterface方法把當前服務對象和描述符進行關聯。在asInterface方法中會調用queryLocalInterface查詢,如果不在同一進程就返回null,這個時候就返回Proxy對象。

上面看完了Stub類之後,發現他其實是遠端服務Binder對象的一個中間者,用來和客戶端進行交互的,下面再來看一下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類是服務端的中間者,一般是實現了AIDL接口類型和繼承了Binder類,具備將Binder對象轉化成原生對象的能力
Proxy類是客戶端的中間者,一般是實現了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屬性,讓它運行在與默認進程不同的進程中。
 

        

\

從上圖可看出客戶端和服務運行在兩個進程當中。

然後點擊按鈕,成功返回結果。

最後給出一張流程圖,加深印象:

\

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved