Android AIDL——進(jìn)程通信機(jī)制詳解
Android AIDL, Android進(jìn)程機(jī)制通信機(jī)制,這里就整理下AIDL 的知識(shí),幫助大家學(xué)習(xí)理解此部分知識(shí)!
什么是 AIDL
AIDL 全稱 Android Interface Definition Language,即 安卓接口描述語言。聽起來很深?yuàn)W,其實(shí)它的本質(zhì)就是生成進(jìn)程間通信接口的輔助工具。它的存在形式是一種 .aidl 文件,開發(fā)者需要做的就是在該文件中定義進(jìn)程間通信的接口,編譯的時(shí)候 IDE 就會(huì)根據(jù)我們的 .aidl 接口文件生成可供項(xiàng)目使用的 .java 文件,這和我們說的“語法糖”有些類似。
AIDL 的語法就是 java 的語法,就是導(dǎo)包上有點(diǎn)細(xì)微差別。java 中如果兩個(gè)類在相同的包中,是不需要進(jìn)行導(dǎo)包操作的,但是在 AIDL 中,則必須進(jìn)行導(dǎo)包聲明。
AIDL 詳解
構(gòu)想一個(gè)場(chǎng)景:我們有一個(gè)圖書管理系統(tǒng),這個(gè)系統(tǒng)的通過 CS 模式來實(shí)現(xiàn)。具體的管理功能由服務(wù)端進(jìn)程來實(shí)現(xiàn),客戶端只需要調(diào)用相應(yīng)的接口就可以。
那么首先定義這個(gè)管理系統(tǒng)的 ADIL 接口。
我們?cè)?/rc 新建 aidl 包,包中有三個(gè)文件 Book.java 、Book.aidl、IBookManager.aidl 三個(gè)文件。
package com.example.aidl book
public class Book implements Parcelable {
int bookId;
String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
...
}
package com.example.aidl;
Parcelable Book;
package com.example.aidl;
import com.example.aidl.Book;
inteface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
下面對(duì)這三個(gè)文件分別進(jìn)行說明:
Book.java 是我們定義的實(shí)體類,它實(shí)現(xiàn)了 Parcelable 接口,這樣 Book 類才能在進(jìn)程間傳輸。
Book.aidl 是這個(gè)實(shí)體類在 AIDL 中的聲明。
IBookManager 是服務(wù)端和客戶端通信的接口。(注意,在 AIDL 接口中除基本類型外,參數(shù)前須加方向,in 表示輸入型參數(shù),out 表示輸出型參數(shù),inout 表示輸入輸出型參數(shù))
編譯器編譯后,android studio 為我們的項(xiàng)目自動(dòng)生成了一個(gè) .java 文件,這個(gè)文件包含三個(gè)類,這三個(gè)類分別是 IBookManager, Stub 和 Proxy,這三個(gè)類都是靜態(tài)類型,我們完全可以把他們分開來,三個(gè)類定義如下:
IBookManager
public interface IBookManager extends android.os.IInterface {
public void addBook(net.bingyan.library.Book book) throws android.os.RemoteException;
public java.util.List<net.bingyan.library.Book> getBookList() throws android.os.RemoteException;
}
Stub
public static abstract class Stub extends android.os.Binder implements net.bingyan.library.IBookManager {
private static final java.lang.String DESCRIPTOR = "net.bingyan.library.IBookManager";
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an net.bingyan.library.IBookManager interface,
* generating a proxy if needed. http://www.manongjc.com/article/1501.html
*/
public static net.bingyan.library.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof net.bingyan.library.IBookManager))) {
return ((net.bingyan.library.IBookManager) iin);
}
return new net.bingyan.library.IBookManager.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_addBook: {
data.enforceInterface(DESCRIPTOR);
net.bingyan.library.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = net.bingyan.library.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<net.bingyan.library.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
Proxy
private static class Proxy implements net.bingyan.library.IBookManager {
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. http://www.manongjc.com/article/1500.html
*/
@Override
public void addBook(net.bingyan.library.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.util.List<net.bingyan.library.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<net.bingyan.library.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(net.bingyan.library.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
對(duì)生成的這三個(gè)類的說明如下:
- IBookManager 這個(gè)類是我們定義的接口,android studio 給它添加了一個(gè)父類,讓它繼承自 android.os.interface 這個(gè)接口,這個(gè)接口只有一個(gè)方法 IBinder asBinder(),這樣 IBookManager 中就有三個(gè)帶實(shí)現(xiàn)的方法了,它是服務(wù)端進(jìn)程和客戶端進(jìn)程通信的窗口。
- Stub 是個(gè)抽象類,這個(gè)類繼承自 android.os.Binder 類,并且實(shí)現(xiàn)的了 IBookManager 這個(gè)接口。在 Stub 中,已經(jīng)實(shí)現(xiàn)了 asBinder() 這個(gè)接口方法,還有兩個(gè)是我們定義的 AIDL 接口方法留給繼承它的子類去實(shí)現(xiàn)。它用在服務(wù)端,因此服務(wù)端需要實(shí)現(xiàn)這兩個(gè)方法。
- Proxy 顧名思義是一個(gè)代理類,它是服務(wù)端在客戶端的一個(gè)代理,它也實(shí)現(xiàn)了 IBookManager接口,并且實(shí)現(xiàn)了 IBookManager 中的所有方法。它用在客戶端,是服務(wù)端在客戶端的代理。現(xiàn)在我們對(duì)這三個(gè)類逐個(gè)分析:
- IBookManager 這個(gè)類沒什么好說的,它只是簡(jiǎn)單繼承了 asInterface 這個(gè)接口,作用就是將 IBookManager 轉(zhuǎn)換成 IBinder。
- Proxy 這個(gè)類上面已經(jīng)提到過了,它就是進(jìn)程間通信機(jī)制的一個(gè)封裝類,他的內(nèi)部實(shí)現(xiàn)機(jī)制就是 Binder,通過構(gòu)造方法我們也容易看出來。它的構(gòu)造方法接受一個(gè) IBinder 類型的參數(shù),參數(shù)名為 remote,顯然,它代表著服務(wù)端。我們看看這個(gè)類中的方法 addBook() 和 getBookList():
@Override
public void addBook(net.bingyan.library.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR)
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override /* http://www.manongjc.com/article/1547.html */
public java.util.List<net.bingyan.library.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<net.bingyan.library.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(net.bingyan.library.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
它們是編譯器自動(dòng)實(shí)現(xiàn)的,這兩個(gè)方法有很多類似之處,可以現(xiàn)在這里透露下:這兩個(gè)方法就是客戶端進(jìn)程調(diào)用服務(wù)端進(jìn)程的窗口。在這兩個(gè)方法的開始,它們都定義了兩個(gè) Parcel(中文譯名:包裹)對(duì)象。Parcel 這個(gè)類我們看上去很眼熟,是的,Book 類中的 writeToParcel() 和 CREATOR中的 createFromParcel() 的參數(shù)就是 Parcel 類型的,關(guān)于這個(gè)類文檔中解釋如下:
Container for a message (data and object references) that can be sent through an IBinder. A Parcel can contain both flattened data that will be unflattened on the other side of the IPC (using the various methods here for writing specific types, or the general {@link Parcelable} interface), and references to live {@link IBinder} objects that will result in the other side receiving a proxy IBinder connected with the original IBinder in the Parcel.
翻譯一下:Proxy 是一個(gè)可以通過 IBinder 進(jìn)行消息傳遞的一個(gè)容器。一個(gè) Parcel 可以包含可序列化的數(shù)據(jù),這些數(shù)據(jù)會(huì)在 IPC 的另一端被反序列化;它也可以包含指向 IBinder 對(duì)象的引用,這會(huì)使得另一端接收到一個(gè) IBinder 類型的代理對(duì)象,這個(gè)代理對(duì)象連接著 Parcel 中的原始 IBinder 對(duì)象。
下面用圖來直觀的說明:

Android 進(jìn)程通信機(jī)制之 AIDL
如圖,我們可以很直觀的看到服務(wù)端以 Parcel 作為數(shù)據(jù)包裹依靠 Binder 和客戶端進(jìn)行通信。數(shù)據(jù)包裹就是序列化之后的對(duì)象。
如上所述,這兩個(gè)方法都定義了兩個(gè) Parcel 對(duì)象,分別叫做 _data 和 _reply,形象的來說,從客戶端的角度來看,_data 就是客戶端發(fā)送給服務(wù)端的數(shù)據(jù)包裹,_reply 服務(wù)端發(fā)送給客戶端的數(shù)據(jù)包裹。
之后便開始用這兩個(gè)對(duì)象來和服務(wù)端進(jìn)行通信了,我們能夠觀察到,兩個(gè)方法中都有這么個(gè)方法調(diào)用 mRemote.transact(),它有四個(gè)參數(shù),第一個(gè)參數(shù)的意義我們后面再講,第二個(gè)參數(shù) _data 負(fù)責(zé)向服務(wù)端發(fā)送數(shù)據(jù)包裹比如接口方法的參數(shù),第三個(gè)參數(shù) _reply 負(fù)責(zé)從服務(wù)端接收數(shù)據(jù)包裹比如接口方法的返回值。這行代碼只有一句簡(jiǎn)單的方法調(diào)用,但是卻是 AIDL 通信的最核心部分,它其實(shí)進(jìn)行了一次遠(yuǎn)程方法調(diào)用(客戶端通過本地代理 Proxy 暴露的接口方法調(diào)用服務(wù)端 Stub 同名方法),所以能想到它是一個(gè)耗時(shí)操作。
在我們的例子中:
1.void addBook(Book book) 需要借助 _data 向服務(wù)端發(fā)送參數(shù) Book:book,發(fā)送的方式就是把 Book 通過其實(shí)現(xiàn)的 writeToParcel(Parcel out) 方法打包至 _data 中,正如你能想到的,_data 其實(shí)就是參數(shù) out,還記得 Book 中的這個(gè)方法的實(shí)現(xiàn)嗎? 我們是將 Book 的字段一個(gè)個(gè)打包至 Parcel 中的。
2.List<Book> getBookList() 需要借助 _reply 從服務(wù)端接收返回值 List<Book>:books,方法中的做法是將 Book 中的 CREATOR 這個(gè)靜態(tài)字段作為參數(shù)傳入 _reply 的 createTypedArrayList() 方法中,還記得 Book 中的 CREATOR 嗎?當(dāng)時(shí)你是不是好奇這個(gè)靜態(tài)字段應(yīng)該怎么用呢?現(xiàn)在一切明了了,我們需要靠這個(gè)對(duì)象(便于理解我們可以叫它”反序列化器“)來對(duì)服務(wù)端的數(shù)據(jù)反序列化從而重新生成可序列化的對(duì)象或者對(duì)象數(shù)組。很明顯 CREATOR 借助 _reply 生成了 List<Book>:books。
當(dāng)然這兩個(gè)方法中的 _data 和 _reply 不僅傳遞了對(duì)象,還傳遞了一些校驗(yàn)信息,這個(gè)我們可以不必深究,但應(yīng)注意的是,Parcel 打包順序和解包順序要嚴(yán)格對(duì)應(yīng)。例如,第一個(gè)打包的是 int:i,那么第一解包的也應(yīng)該是這個(gè)整型值。也即打包時(shí)第一次調(diào)用的如果是 Parcel.writeInt(int),解包時(shí)第一次調(diào)用的應(yīng)該是 Parcel.readInt()。
到此,客戶端的 Proxy 講解完了,下面我們看看服務(wù)端的 Stub。
Stub 中實(shí)現(xiàn)了 IBookManager 的其中一個(gè)方法,這個(gè)很簡(jiǎn)單,就是簡(jiǎn)單的將自身返回,因?yàn)?Stub 本身就繼承自 Binder,而 Binder 繼承自 IBinder,所以沒有任何問題。你會(huì)問:還有兩個(gè)方法沒實(shí)現(xiàn)呢?這兩個(gè)方法就是我們定義的接口方法,它們留給服務(wù)端進(jìn)程去實(shí)現(xiàn),也就是說,到時(shí)候我們?cè)诜?wù)端進(jìn)程中需要定義一個(gè) Stub 的實(shí)現(xiàn)者。下面對(duì) Stub 中的兩個(gè)重要方法進(jìn)行分析:
IBookManager asInterface(IBinder obj)
public static net.bingyan.library.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof net.bingyan.library.IBookManager))) {
return ((net.bingyan.library.IBookManager) iin);
}
return new net.bingyan.library.IBookManager.Stub.Proxy(obj);
}
這個(gè)方法的作用是將 Stub 類轉(zhuǎn)換成 IBookManager 這個(gè)接口,方法中有個(gè)判斷:如果我們的服務(wù)端進(jìn)程和客戶端進(jìn)程是同一進(jìn)程,那么就直接將 Stub 類通過類型轉(zhuǎn)換轉(zhuǎn)成 IBookManager;如果不是同一進(jìn)程,那么就通過代理類 Proxy 將 Stub 轉(zhuǎn)換成 IBookManager。為什么這么做,我們知道如果服務(wù)端進(jìn)程和客戶端進(jìn)程不是同一進(jìn)程,那么它們的內(nèi)存就不能共享,就不能通過一般的方式進(jìn)行通信,但是我們?nèi)绻约喝?shí)現(xiàn)進(jìn)程間通信方式,對(duì)于普通開發(fā)者來說成本太大,因此編譯器幫我們生成了一個(gè)封裝了了進(jìn)程間通信的工具,也就是這個(gè) Proxy,這個(gè)類對(duì)底層的進(jìn)程通信機(jī)制進(jìn)行了封裝只同時(shí)暴露出接口方法,客戶端只需要調(diào)用這兩個(gè)方法實(shí)現(xiàn)進(jìn)程間通信(其實(shí)就是方法的遠(yuǎn)程調(diào)用)而不需要了解其中的細(xì)節(jié)。
有了這個(gè)方法,我們?cè)诳蛻舳丝梢越柚鋵⒁粋€(gè) IBinder 類型的變量轉(zhuǎn)換成我們定義的接口 IBookManager,它的使用場(chǎng)景我們會(huì)在后面的實(shí)例中進(jìn)行講解。
onTransact(int code, Parcel data, Parcel reply, int flags)
@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_addBook: {
data.enforceInterface(DESCRIPTOR);
net.bingyan.library.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = net.bingyan.library.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true; /* http://www.manongjc.com/article/1499.html */
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<net.bingyan.library.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
這個(gè)方法我們是不是也很熟悉呢?我們?cè)?Proxy 中也看到一個(gè)類似得方法 transact(int, Parcel, Parcel, int),它們的參數(shù)一樣,而且它們都是 Binder 中的方法,那么它們有什么聯(lián)系呢?
前面說了,transact() 執(zhí)行了一個(gè)遠(yuǎn)程調(diào)用,如果說 transact() 是遠(yuǎn)程調(diào)用的發(fā)起,那么 onTransact() 就是遠(yuǎn)程調(diào)用的響應(yīng)。真實(shí)過程是客戶端發(fā)器遠(yuǎn)程方法調(diào)用,android 系統(tǒng)通過底層代碼對(duì)這個(gè)調(diào)用進(jìn)行響應(yīng)和處理,之后回調(diào)服務(wù)端的 onTransact() 方法,從數(shù)據(jù)包裹中取出方法參數(shù),交給服務(wù)端實(shí)現(xiàn)的同名方法調(diào)用,最后將返回值打包返回給客戶端。
需要注意的是 onTransact() 是在服務(wù)端進(jìn)程的 Binder 線程池中進(jìn)行的,這就意味著如果我們的要在 onTransact() 方法的中更新 UI,就必須借助 Handler。
這兩個(gè)方法的第一個(gè)參數(shù)的含義是 AIDL 接口方法的標(biāo)識(shí)碼,在 Stub 中,定義了兩個(gè)常量作為這兩個(gè)方法的標(biāo)示:
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
如果 code == TRANSACTION_addBook,那么說明客戶端調(diào)用的是 addBook();如果 code == TRANSACTION_getBookList,那么客戶端調(diào)用的是 getBookList(),然后交由相應(yīng)的服務(wù)端方法處理。 用一張圖來表示整個(gè)通信過程:
Android 進(jìn)程通信機(jī)制之 AIDL
了解了 AIDL 的整個(gè)過程,接下來就是 AIDL 在安卓程序中的應(yīng)用了。
AIDL 的使用
相信大家應(yīng)該都和清楚 Service 的使用了吧,Service 雖然稱作“服務(wù)”,并且運(yùn)行于后臺(tái),但是它們默認(rèn)還是運(yùn)行在默認(rèn)進(jìn)程的主線程中。其實(shí)讓 Service 運(yùn)行在默認(rèn)進(jìn)程中,有點(diǎn)大材小用了。android 的很多系統(tǒng)服務(wù)都運(yùn)行于單獨(dú)的進(jìn)程中,供其他應(yīng)用調(diào)用,比如窗口管理服務(wù)。這樣做的好處是可以多個(gè)應(yīng)用共享同一個(gè)服務(wù),節(jié)約了資源,也便于集中管理各個(gè)客戶端,要注意問題的就是線程安全問題。
那么接下來我們就用 AIDL 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 CS 架構(gòu)的圖書管理系統(tǒng)。
首先我們定義服務(wù)端:
BookManagerService
public class BookManagerService extends Service {
private final List<Book> mLibrary = new ArrayList<>();
private IBookManager mBookManager = new IBookManager.Stub() {
@Override
public void addBook(Book book) throws RemoteException {
synchronized (mLibrary) {
mLibrary.add(book);
Log.d("BookManagerService", "now our library has " + mLibrary.size() + " books");
}
}
@Override
public List<Book> getBookList() throws RemoteException {
return mLibrary;
}
};
@Override /* http://www.manongjc.com/article/1496.html */
public IBinder onBind(Intent intent) {
return mBookManager.asBinder();
}
}
<service android:process=":remote" android:name=".BookManagerService"/>
服務(wù)端我們定義了 BookManagerService 這個(gè)類,在它里面我們創(chuàng)建了服務(wù)端的 Stub 對(duì)象,并且實(shí)現(xiàn)了需要實(shí)現(xiàn)的兩個(gè) AIDL 接口方法來定義服務(wù)端的圖書管理策略。在 onBind() 方法中我們將 IBookManager 對(duì)象作為 IBinder 返回。我們知道,當(dāng)我們綁定一個(gè)服務(wù)時(shí),系統(tǒng)會(huì)調(diào)用 onBinder() 方法得到服務(wù)端的 IBinder 對(duì)象,并將其轉(zhuǎn)換成客戶端的 IBinder 對(duì)象傳給客戶端,雖然服務(wù)端的 IBinder 和 客戶端的 IBinder 是兩個(gè) IBinder 對(duì)象,但他們?cè)诘讓佣际峭粋€(gè)對(duì)象。我們?cè)?xml 中注冊(cè) Service 時(shí)給它指定了進(jìn)程名,這樣 Service 就能運(yùn)行在單獨(dú)的進(jìn)程中了。
接下來看看客戶端的實(shí)現(xiàn):
Client
public class Client extends AppCompatActivity {
private TextView textView;
private IBookManager bookManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.library_book_manager_system_client);
Intent i = new Intent(Client.this, BookManagerService.class);
bindService(i, conn, BIND_AUTO_CREATE);
Button addABook = (Button) findViewById(R.id.button);
addABook.setOnClickListener(v -> {
if (bookManager == null) return;
try {
bookManager.addBook(new Book(0, "book"));
textView.setText(getString(R.string.book_management_system_book_count, String.valueOf(bookManager.getBookList().size())));
} catch (RemoteException e) {
e.printStackTrace();
}
});
textView = (TextView) findViewById(R.id.textView);
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("Client -->", service.toString());
bookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("Client", name.toString());
}
};
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1"
android:gravity="center">
<Button
android:text="http://www.manongjc.com/article/1495.html"
android:layout_width="111dp"
android:layout_height="wrap_content"
android:id="@+id/button" />
<TextView
android:layout_marginTop="10dp"
android:text="@string/book_management_system_book_count"
android:layout_width="231dp"
android:gravity="center"
android:layout_height="wrap_content"
android:id="@+id/textView" />
</LinearLayout>
我們的客戶端就是一個(gè) Activity,onCreate() 中進(jìn)行了服務(wù)的綁定,bindService() 方法中有一參數(shù) ServiceConnection:conn,因?yàn)榻壎ǚ?wù)是異步進(jìn)行的,這個(gè)參數(shù)的作用就是綁定服務(wù)成功后回調(diào)的接口,它有兩個(gè)回調(diào)方法:一個(gè)是連接服務(wù)成功后回調(diào),另一個(gè)在與服務(wù)端斷開連接后回調(diào)。我們現(xiàn)在關(guān)心的主要是 onServiceConnected() 方法,在這里我們只做了一件事:將服務(wù)端轉(zhuǎn)換過來的 IBinder 對(duì)象轉(zhuǎn)換成 AIDL 接口,我們定義 IBookManager:bookManager 字段來保持對(duì)其的引用。這樣的話,我們就可以通過這個(gè) bookManager 來進(jìn)行方法的遠(yuǎn)程調(diào)用。我們給客戶端的 Button 注冊(cè)事件:每一次點(diǎn)擊都會(huì)向服務(wù)端增加一本書,并且將圖書館現(xiàn)有的圖書數(shù)量顯示出來。
現(xiàn)在我們看看程序的運(yùn)行效果:

每當(dāng)我們點(diǎn)擊按鈕,我們就成功的向服務(wù)端添加了一本書,說明我們通過 AIDL 跨進(jìn)程通信成功了。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Kotlin 高階函數(shù)與Lambda表達(dá)式示例詳解
這篇文章主要為大家介紹了Kotlin 高階函數(shù)與Lambda表達(dá)式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Android實(shí)現(xiàn)滑動(dòng)選擇控件實(shí)例代碼
本篇文章主要介紹了Android實(shí)現(xiàn)滑動(dòng)選擇控件實(shí)例代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2017-03-03
很詳細(xì)的android序列化過程Parcelable
這篇文章主要為大家詳細(xì)介紹了很詳細(xì)的android序列化過程Parcelable,代碼注釋很詳細(xì),感興趣的小伙伴們可以參考一下2016-08-08
Android?App跳轉(zhuǎn)微信小程序踩坑實(shí)戰(zhàn)
現(xiàn)在市面上很多的應(yīng)用都可以實(shí)現(xiàn)相互跳轉(zhuǎn),下面這篇文章主要給大家介紹了關(guān)于Android?App跳轉(zhuǎn)微信小程序踩坑的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05
使用RecyclerView實(shí)現(xiàn)Item點(diǎn)擊事件
這篇文章主要介紹了使用RecyclerView實(shí)現(xiàn)Item點(diǎn)擊事件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Android實(shí)現(xiàn)圖片的裁剪(不調(diào)用系統(tǒng)功能)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)圖片的裁剪,不調(diào)用系統(tǒng)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android簡(jiǎn)易圖片瀏覽器的實(shí)現(xiàn)
最近做了一個(gè)圖片瀏覽小程序,本文主要介紹了Android簡(jiǎn)易圖片瀏覽器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03
Flutter實(shí)現(xiàn)抽屜動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了Flutter實(shí)現(xiàn)抽屜動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

