使用AIDL實現Android的跨進程通信
AIDL(Android Interface Definition Language), 即Android接口定義語言. 在Android中, AIDL是跨進程通信的主要實現方式. 我們同樣也可以使用AIDL, 實現自己的跨進程方案. 本文介紹AIDL的使用方式.
AIDL
服務端: 創建Service服務監聽客戶端的請求, 實現AIDL接口.
客戶端: 綁定服務端, 調用AIDL的方法.
AIDL接口: 跨進程通信的接口, AIDL的包名需要與項目的包名相同, 默認生成即可.
AIDL支持的數據類型: 基本類型, 字符串類型(String&CharSequence), List, Map, Parcelable, AIDL接口. 共六種.
流程: 客戶端注冊服務端, 服務端添加新書, 客戶端接收, 并提供客戶端的查詢書數量的接口.
本文源碼的GitHub下載地址
AIDL
本文使用自定義的數據類型Book類, 實現Parcelable接口, 具體參考.
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
@Override public int describeContents() {
return 0;
}
@Override public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
@Override public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override public Book[] newArray(int size) {
return new Book[size];
}
};
private Book(Parcel source) {
bookId = source.readInt();
bookName = source.readString();
}
@Override public String toString() {
return "ID: " + bookId + ", BookName: " + bookName;
}
}
AIDL使用自定義類, 需要聲明Parcelable類.
// IBook.aidl
package org.wangchenlong.wcl_aidl_demo;
// Declare any non-default types here with import statements
parcelable Book;
添加AIDL的接口, 用于通知新書到達.
// IOnNewBookArrivedListener.aidl
package org.wangchenlong.wcl_aidl_demo;
// Declare any non-default types here with import statements
import org.wangchenlong.wcl_aidl_demo.Book;
interface IOnNewBookArrivedListener {
/**
* 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);
void onNewBookArrived(in Book newBook);
}
AIDL文件注釋較多, 都是自動生成, 不影響閱讀.
核心AIDL類, 書籍管理器, 四個方法, 獲取圖書列表, 添加書籍, 注冊接口, 解注冊接口. 注意, 使用其他方法, 需要import導入相應文件.
// IBookManager.aidl
package org.wangchenlong.wcl_aidl_demo;
// Declare any non-default types here with import statements
import org.wangchenlong.wcl_aidl_demo.Book;
import org.wangchenlong.wcl_aidl_demo.IOnNewBookArrivedListener;
interface IBookManager {
/**
* 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);
List<Book> getBookList(); // 返回書籍列表
void addBook(in Book book); // 添加書籍
void registerListener(IOnNewBookArrivedListener listener); // 注冊接口
void unregisterListener(IOnNewBookArrivedListener listener); // 注冊接口
}
所有的參數都需要標注參數方向, in表示輸入類型, out表示輸出類型, inout表示輸入輸出類型. out與inout的開銷較大, 不能統一使用高級方向.
服務端
服務端通過Binder實現AIDL的IBookManager.Stub
接口.
private Binder mBinder = new IBookManager.Stub() {
@Override public List<Book> getBookList() throws RemoteException {
SystemClock.sleep(5000); // 延遲加載
return mBookList;
}
@Override public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
int num = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.e(TAG, "添加完成, 注冊接口數: " + num);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
int num = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.e(TAG, "刪除完成, 注冊接口數: " + num);
}
};
服務啟動時, 添加兩本新書, 并使用線程繼續添加.
@Override public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "iOS"));
new Thread(new ServiceWorker()).start();
}
添加書籍, 并發送通知.
private class ServiceWorker implements Runnable {
@Override public void run() {
while (!mIsServiceDestroyed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
num++;
if (num == 5) {
mIsServiceDestroyed.set(true);
}
Message msg = new Message();
mHandler.sendMessage(msg); // 向Handler發送消息,更新UI
}
}
}
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
int bookId = 1 + mBookList.size();
Book newBook = new Book(bookId, "新書#" + bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
向注冊監聽的, 發送新書的添加通知.
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
Log.e(TAG, "發送通知的數量: " + mBookList.size());
int num = mListenerList.beginBroadcast();
for (int i = 0; i < num; ++i) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
Log.e(TAG, "發送通知: " + listener.toString());
listener.onNewBookArrived(book);
}
mListenerList.finishBroadcast();
}
在AndroidManifest中, Service與Activity不在同一進程.
<!--與主應用不在同一進程中-->
<service
android:name=".BookManagerService"
android:process=":remote"/>
客戶端
綁定服務和解綁服務, 綁定服務的具體內容, 都是在mConnection
中實現.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.main_tv_book_list);
}
@Override protected void onDestroy() {
if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) {
try {
Log.e(TAG, "解除注冊");
mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
添加內容, 注冊監聽接口.
private ServiceConnection mConnection = new ServiceConnection() {
@Override public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
mRemoteBookManager = bookManager;
Book newBook = new Book(3, "學姐的故事");
bookManager.addBook(newBook);
new BookListAsyncTask().execute();
bookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override public void onServiceDisconnected(ComponentName name) {
mRemoteBookManager = null;
Log.e(TAG, "綁定結束");
}
};
當調用監聽接口時, 異步顯示圖書列表.
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override public void onNewBookArrived(Book newBook) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
}
};
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.e(TAG, "收到的新書: " + msg.obj);
new BookListAsyncTask().execute();
break;
default:
super.handleMessage(msg);
break;
}
}
};
點擊綁定服務
按鈕, 執行綁定服務. 點擊獲取圖書數量
按鈕, 獲取當前列表的數量.
效果
效果
Android跨進程通信比較復雜, 但是意義重大, 目前常用的動態加載框架都需要處理跨進程通信等問題, 熟練基本原理, 掌握使用方式.
OK, that's all! Enjoy it!
文/SpikeKing(簡書)