AIDL 的使用和代碼分析

一.相關(guān)介紹

在 Android 系統(tǒng)中,進(jìn)程間通信 (IPC) 是一種很重要的機(jī)制。IPC 產(chǎn)生的原因是某些情況下需要在兩個(gè)進(jìn)程之間進(jìn)行一些數(shù)據(jù)的交換。而在深入學(xué)習(xí) Android 的過程中難免會(huì)遇到 IPC 的相關(guān)問題,比如常見的有在自己的應(yīng)用程序中讀取手機(jī)聯(lián)系人的信息,這就涉及到 IPC 了。因?yàn)樽约旱膽?yīng)用程序是一個(gè)進(jìn)程,通訊錄也是一個(gè)進(jìn)程,只不過獲取通訊錄的數(shù)據(jù)信息是通過 Content Provider 的方式來實(shí)現(xiàn)的。

對(duì)于初學(xué)者來說,在一開始接觸 IPC 時(shí)可能會(huì)摸不著頭腦,因?yàn)榫W(wǎng)上很多博客在講 Android IPC 時(shí)通常都是長(zhǎng)篇大論,沒有從例子著手?;谝陨戏N種原因以及希望對(duì) AIDL 有一個(gè)更深入的理解,本篇博文就誕生了。在 Android 系統(tǒng)中,IPC 的方式有很多種,比如有 Messenger 、AIDL 和 ContentProvider 等。我們今天就來講講其中的 AIDL ,AIDL 也是比較常見和經(jīng)常使用的一種 IPC 方式。希望讀者在看完本篇之后對(duì)于 AIDL 有一個(gè)比較深入的理解。

什么是 AIDL?

AIDL 的全稱是 Android Interface Definition Language(即 Android 接口定義語言)。

AIDL是Binder的實(shí)例。

AIDL的使用實(shí)例:

我們來模擬一下需要進(jìn)行 IPC 的情況,現(xiàn)在有客戶端和服務(wù)端,客戶端通過 AIDL 來和服務(wù)端進(jìn)行 IPC 。我們假定現(xiàn)在客戶端需要傳一個(gè) Person 類的對(duì)象給服務(wù)端,之后服務(wù)端回傳給客戶端一個(gè) Person 類的集合。

1.服務(wù)端的相關(guān)代碼

以下 Person.aidl 文件:

parcelable Person;

注意在 IPC 機(jī)制中傳遞的自定義對(duì)象需要序列化,所以要實(shí)現(xiàn) Parcelable 接口。在 AIDL 文件中使用parcelable關(guān)鍵字聲明。有了 Person.aidl 之后,我們就要?jiǎng)?chuàng)建 AIDL 接口了。

interface AddPersonInter {
List<Person> addPerson(in Person person);
}

在 IMyAidlInterface.aidl 里,主要聲明一個(gè)用于添加 Person 對(duì)象的抽象方法。另外,需要注意以下幾點(diǎn):

1.Person 類需要手動(dòng)去 import ,在 AIDL 文件中不能自動(dòng)導(dǎo)包;

2.在addPerson方法里需要聲明參數(shù)是 in 的,用來表示該參數(shù)是傳入的。除了 in 之外,還有 out 和 inout ;

下面我們要?jiǎng)?chuàng)建一個(gè) Service 用于和客戶端進(jìn)行 IPC 。這里還要把該 Service 運(yùn)行在一個(gè)新的進(jìn)程里。我們只要在 AndroidManifest.xml 中聲明android:process=":remote"就行了。

public class MyService extends Service {
private static final String TAG = "MyService";

private List<Person> persons = new ArrayList<>();

public MyService() {
}

@Override
public IBinder onBind(Intent intent) {
    Log.e("aaaa", "binder綁定成功");
    return binder;
}

private final IBinder binder = new AddPersonInter.Stub() {
    @Override
    public List<Person> addPerson(Person person) throws RemoteException {
        synchronized (persons) {
            persons.add(person);
            Log.e("aaaaa", "服務(wù)端  name----" + person.getName() + "       age===" + person.getAge());
            return persons;
        }
    }
};
}

在上面的代碼中我們可以看到在onBind(Intent intent)方法中返回了 mBinder ,而客戶端正是通過這個(gè) mBinder 來和服務(wù)端進(jìn)行 IPC 的。mBinder 是 IMyAidlInterface.Stub 匿名類的對(duì)象,IMyAidlInterface.Stub 其實(shí)是一個(gè)抽象類,繼承自 Binder ,實(shí)現(xiàn)addPerson方法。這里要注意以下,在addPerson的方法中需要將 persons 同步,這是因?yàn)樵诜?wù)端 AIDL 是運(yùn)行在 Binder 線程池中的,有可能會(huì)有多個(gè)客戶端同時(shí)連接,這時(shí)候就需要同步以防止數(shù)據(jù)出錯(cuò)。

2.客戶端的相關(guān)代碼

客戶端需要將服務(wù)端的aidl文件夾整體復(fù)制到客戶端,并將用到到的java類Person.java復(fù)制到客戶端,
注意包名一致,不然編譯會(huì)報(bào)錯(cuò)。

public class Main2Activity extends AppCompatActivity {

TextView tv;
private AddPersonInter addPersonInter;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    tv=this.findViewById(R.id.tv);
    // 啟動(dòng)服務(wù)端的服務(wù),并進(jìn)行綁定
    Intent intent = new Intent();
    intent.setComponent(new ComponentName("com.seventeenok.test", "com.seventeenok.test.MyService"));
    bindService(intent, conn, Context.BIND_AUTO_CREATE);

    tv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<Person> list = addPersonInter.addPerson(new Person("lizhi", 24));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    });
}

private ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        addPersonInter = AddPersonInter.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        addPersonInter = null;
    }
};
}

AIDL 的流程基本上就是這樣的。通過這個(gè)簡(jiǎn)單的例子,相信對(duì)于 AIDL 有了一個(gè)初步的了解。下面我們就要進(jìn)行 AIDL 的代碼分析。

3.項(xiàng)目的整體目錄結(jié)構(gòu):
aaa.png

AIDL代碼分析

工程中的 gen 目錄下找到對(duì)應(yīng) AIDL 編譯后的文件:

public interface AddPersonInter extends android.os.IInterface {
/**
 * Local-side IPC implementation stub class.
 */
public static abstract class Stub extends android.os.Binder implements com.seventeenok.test.AddPersonInter {
    private static final java.lang.String DESCRIPTOR = "com.seventeenok.test.AddPersonInter";

    /**
     * Construct the stub at attach it to the interface.
     */
    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }

    /**
     * Cast an IBinder object into an com.seventeenok.test.AddPersonInter interface,
     * generating a proxy if needed.
     */
    public static com.seventeenok.test.AddPersonInter asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.seventeenok.test.AddPersonInter))) {
            return ((com.seventeenok.test.AddPersonInter) iin);
        }
        return new com.seventeenok.test.AddPersonInter.Stub.Proxy(obj);
    }

    @Override
    public android.os.IBinder asBinder() {
        return this;
    }

    @Override
    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_addPerson: {
                data.enforceInterface(DESCRIPTOR);
                com.seventeenok.test.Person _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.seventeenok.test.Person.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                java.util.List<com.seventeenok.test.Person> _result = this.addPerson(_arg0);
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }

    private static class Proxy implements com.seventeenok.test.AddPersonInter {
        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;
        }

        @Override
        public java.util.List<com.seventeenok.test.Person> addPerson(com.seventeenok.test.Person person) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            java.util.List<com.seventeenok.test.Person> _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                if ((person != null)) {
                    _data.writeInt(1);
                    person.writeToParcel(_data, 0);
                } else {
                    _data.writeInt(0);
                }
                mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                _reply.readException();
                _result = _reply.createTypedArrayList(com.seventeenok.test.Person.CREATOR);
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }

    static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

public java.util.List<com.seventeenok.test.Person> addPerson(com.seventeenok.test.Person person) throws android.os.RemoteException;

}

可以看到編譯后的 AddPersonInter.aidl 變成了一個(gè)接口,繼承自 IInterface 。在 AddPersonInter接口中我們發(fā)現(xiàn)主要分成兩部分結(jié)構(gòu):抽象類 Stub 和原來 aidl 中聲明的addPerson方法。

重點(diǎn)在于 Stub 類,下面我們來分析一下。從 Stub 類中我們可以看到是繼承自 Binder 并且實(shí)現(xiàn)了 AddPersonInter接口。 Stub 類的基本結(jié)構(gòu)如下:

asInterface(android.os.IBinder obj)方法;

asBinder()方法;

onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)方法;

靜態(tài)類Proxy,主要方法是addPerson(com.seventeenok.test.Person person);

靜態(tài)常量TRANSACTION_addPerson;

asInterface(android.os.IBinder obj)

我們先從asInterface(android.os.IBinder obj)方法入手,在上面的代碼中可以看到,主要的作用就是根據(jù)傳入的Binder對(duì)象轉(zhuǎn)換成客戶端需要的 AddPersonInter接口。如果服務(wù)端和客戶端處于同一個(gè)進(jìn)程,那么該方法得到的就是服務(wù)端 Stub 對(duì)象本身,也就是上面 AIDL 例子 MyService 中的 mBinder 對(duì)象;否則返回的是系統(tǒng)封裝后的 Stub.Proxy ,也就是一個(gè)代理類,在這個(gè)代理中實(shí)現(xiàn)跨進(jìn)程通信。

asBinder()

該方法就是返回當(dāng)前的 Binder 對(duì)象。

onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)

在onTransact方法中,根據(jù)傳入的 code 值會(huì)去執(zhí)行服務(wù)端相對(duì)應(yīng)的方法。其中靜態(tài)變量TRANSACTION_addPerson就是其中的 code 值之一(在 AIDL 文件中聲明的方法有多少個(gè)就有多少個(gè)對(duì)應(yīng)的 code )。其中 data 就是服務(wù)端方法中所需要的參數(shù),執(zhí)行完后,最后把方法的返回結(jié)果放入 reply 中傳遞給客戶端。若該方法返回 false ,那么客戶端請(qǐng)求失敗。

Proxy中的addPerson(com.seventeenok.test.Person person)

Proxy 類是實(shí)現(xiàn)了 AddPersonInter接口,把其中的addPerson方法進(jìn)行了重寫。在方法中一開始創(chuàng)建了兩個(gè) Parcel 對(duì)象,其中一個(gè)用來把方法的參數(shù)裝入,然后調(diào)用transact方法執(zhí)行服務(wù)端的代碼,執(zhí)行完后把返回的結(jié)果裝入另外一個(gè) Parcel 對(duì)象中返回。

看完上面方法的介紹,我們回過頭來看看 AIDL 例子中實(shí)現(xiàn)的流程。在客戶端中通過 Intent 去綁定一個(gè)服務(wù)端的 Service 。在onServiceConnected(ComponentName name, IBinder service)方法中通過返回的 service 可以得到對(duì)應(yīng)的 AIDL 接口的實(shí)例。這是調(diào)用了asInterface(android.os.IBinder obj)方法來完成的。

客戶端在onServiceConnected(ComponentName name, IBinder service)中得到的 service 正是服務(wù)端中的 mBinder 。當(dāng)客戶端調(diào)用 AIDL 接口時(shí),AIDL 通過 Proxy 類中的addPerson來調(diào)用transact方法,transact方法又會(huì)去調(diào)用服務(wù)端的onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)方法。onTransact方法是運(yùn)行在服務(wù)端的 Binder 線程池中的。在onTransact中根據(jù) code 執(zhí)行相關(guān) AIDL 接口的方法,方法的參數(shù)從 data 中獲取。執(zhí)行完畢之后把結(jié)果裝入 reply 中返回給客戶端。 AIDL 的流程基本上就是這樣子了。

總結(jié)

AIDL 在 Android IPC 機(jī)制中算得上是很重要的一部分,AIDL 主要是通過 Binder 來實(shí)現(xiàn)進(jìn)程通信的。當(dāng)然,上面只是簡(jiǎn)單的例子分析AIDL的整個(gè)流程,并沒有涉及到死亡代理、權(quán)限驗(yàn)證等功能,有想法的同學(xué)可以深入研究下。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容