Android IPC程序間通訊(四)AIDL
AIDL-Android介面定義語言
一·
1.相比於Messenger AIDL可跨程序呼叫方法。
2.支援資料型別:
(1) Java 的原生基本型別(int, long, char, boolean, double等)
(2)String 和CharSequence
(3) ArrayList 和 HashMap ,裡面的元素必須是AIDL支援的資料型別; 以上三種類型都不需要匯入(import)
(4) AIDL 自動生成的介面 需要匯入(import)
(5) 實現android.os.Parcelable 介面的類. 需要匯入(import)
3.除了Java 的原生基本型別其他型別引數都需標明方向:in 輸入 , out 輸出, inout 出入輸出。
4.建議吧所有一AIDL相關檔案放在同一個資料夾下,方便複製到客戶端,而且服務端可客戶端AIDL包結構需保持一致,否則 無法反序列化成功
5.優點:功能強大,支援一對多併發通訊,支援即時通訊。
缺點:稍複雜,需做好執行緒同步。
使用場景:一對多且有RPC-遠端方法呼叫 需求。
二,RemoteCallbackList系統用於管理跨程序listener,支援管理任意的AIDL
public class RemoteCallbackList<E extends IInterfaxc>
內部提供一個Map來管理這些AIDL,key為IBinder,value為Callback
ArrayMap<IBinder,Callback> mCallbacks = new ArrayMap<>();
當客戶端註冊listener時,Callback將listener的資訊以IBinder為key存入mCallbacks,刪除時也是找到此key對應的listener進行刪除,因為跨程序生成的是相同的兩個物件
三,建立AIDL檔案,此程式碼複製貼上即可順利執行
1.建立Book類,IPC程序間通訊(三)中已講解,此處再建立一個aidl介面IOnNewBookArriveListener.aidl用於訂閱書籍,同樣會生成它自己的java類
package com.songfeng.aidlautocreate;
// Declare any non-default types here with import statements
//自定義類都需要顯示的引入進來
import com.songfeng.aidlautocreate.Book;
//提供一個監聽新書到達的介面
interface IOnNewBookArriveListener {
void onNewBookArrived(in Book book);
}
2.在IBookManger.aidl介面中增加註冊和解註冊方法,用於被遠端呼叫,以實現某種功能
// IBookManager.aidl
package com.songfeng.aidlautocreate;
// Declare any non-default types here with import statements
import com.songfeng.aidlautocreate.Book;
import com.songfeng.aidlautocreate.IOnNewBookArriveListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerLister(IOnNewBookArriveListener listener);
void unRegisterLister(IOnNewBookArriveListener listener);
}
3.建立BookManagerService用於管理AIDL檔案,在其中實現Binder,並返回給客戶端,以便在客戶端呼叫Binder中的方法。
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList();
//註冊和解註冊多程序中的監聽方法
private RemoteCallbackList mListenerList = new RemoteCallbackList();
@Override
public void onCreate() {
super.onCreate();
new Thread(new arrivedBookRunnable()).start();
}
//自動生成的java類,建立Binder,重寫其方法,實現邏輯
private IBookManager.Stub mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
if (!mBookList.contains(book)){
mBookList.add(book);
}
}
@Override
public void registerLister(IOnNewBookArriveListener listener) throws RemoteException {
mListenerList.register(listener);
}
@Override
public void unRegisterLister(IOnNewBookArriveListener listener) throws RemoteException {
mListenerList.unregister(listener);
}
};
//在這進行一個許可權驗證,只有客戶端註冊了此自定義許可權才返回Binder給客戶端
@Override
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("com.songfeng.aidlautocreate.permission.BookManagerService");
if (check == PackageManager.PERMISSION_DENIED){
return null;
}
return mBinder;
}
/**
* 開啟執行緒每5秒新增一本新書並通知每一個註冊了註冊了監聽方法的客戶端
*/
private class arrivedBookRunnable implements Runnable{
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId,"Android : " + bookId);
//新書到達,啟動通知
oOnNewBookArrived(newBook);
}
}
/**
* 通知每一個註冊可監聽方法IOnNewBookArriveListener的客戶端,並回調到客戶端的onNewBookArrived()
* @param newBook
*/
private void oOnNewBookArrived(Book newBook){
int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnNewBookArriveListener listener =(IOnNewBookArriveListener) mListenerList.getBroadcastItem(i);
try {
listener.onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mListenerList.finishBroadcast();
}
}
4.在客戶端的連線方法中獲取Binder,即可呼叫Binder中方法
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int MSG_BOOK_ARRIVED = 1;
//自定義的AID檔案,裡面是一個介面
private IBookManager mIBookManager;
private List<Book> mBookList;
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new MyHandler();
Intent intent = new Intent(MainActivity.this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_BOOK_ARRIVED:
Book book = (Book) msg.obj;
Toast.makeText(MainActivity.this, "新書 ID:"
+ book.getBookId() + ", 書名 :" + book.getBookName()
+ "已到達。", Toast.LENGTH_SHORT).show();
default:
break;
}
}
}
/**
* 監聽圖書到達,當服務呼叫新書到達的方法時就會回撥到此方法
*/
private IOnNewBookArriveListener mBookArriveListener = new IOnNewBookArriveListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
mHandler.obtainMessage(MSG_BOOK_ARRIVED, book).sendToTarget();
}
};
/**
* 從服務中取出IBinder建立IBookManager
* public static com.songfeng.aidlautocreate.IBookManager asInterface(android.os.IBinder obj)
*/
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IBookManager bookManager = IBookManager.Stub.asInterface(iBinder);
try {
Toast.makeText(MainActivity.this, "連結伺服器成功", Toast.LENGTH_SHORT).show();
mIBookManager = bookManager;
mBookList = bookManager.getBookList();
Log.d(TAG, "BookList.size :" + mBookList.size());
//註冊監聽
bookManager.registerLister(mBookArriveListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Toast.makeText(MainActivity.this, "伺服器已斷開,重連中...", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
};
@Override
protected void onDestroy() {
super.onDestroy();
if (mIBookManager != null && mIBookManager.asBinder().isBinderAlive()) {
try {
mIBookManager.unRegisterLister(mBookArriveListener);
} catch (RemoteException e) {
e.printStackTrace();
}
unbindService(mConnection);
}
}
}
5.AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
. . . . .
<service
android:name=".BookManagerService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
</manifest>