AIDL
-
服務(wù)端創(chuàng)建一個(gè) Service 監(jiān)聽客戶端的鏈接請(qǐng)求净蚤,將 AIDL 的實(shí)現(xiàn)回調(diào)給客戶端刽脖;
客戶端通過 aidl 就可以直接調(diào)用服務(wù)端的方法
-
AIDL 的聲明注意點(diǎn):
- C/S 兩端必須完全一致雇盖,包名都不能錯(cuò)膜楷,否則找不到
- AIDL 中支持的數(shù)據(jù)類型不多:
- 基本數(shù)據(jù)類型滋恬、String浅乔、char
- List 只支持 ArrayList,Map 只支持 HashMap礁竞,且每個(gè)元素都必須被 AIDL 所支持
- AIDL 支持的其實(shí)是抽象的 List 和 Map糖荒,并在最終返回 ArrayList 和 HashMap
- 在 Server 方法中,可以使用其他數(shù)據(jù)類型模捂,比如 CopyOnWriteArrayList 和 ConcurrentHashMap
- parcelable 對(duì)象(必須顯示 import)
- 必須聲明一個(gè)同樣的 aidl 文件捶朵,聲明其為 parcelable 對(duì)象
- AIDL 接口本身(必須顯示 import)
- 除基本類型外,所有入?yún)⒈仨殬?biāo)明 in / out / inout
- in 代表輸入型參數(shù)狂男,out 代表輸出型參數(shù)综看,inout 表示輸入輸出型參數(shù)
- 只支持方法,不支持靜態(tài)常量
- AIDL 的方法是在 Binder 線程池中執(zhí)行的岖食,所以一般需要處理線程同步問題
- 使用 CopyOnWriteArrayList红碑、ConcurrentHashMap、AtomicBoolean
- 如果有接口回調(diào),從 binder 回調(diào)到 app
- 使用 aidl 而不是普通接口
- 注冊(cè)析珊、解注冊(cè)需要使用 RemoteCallbackListener羡鸥,而不是常規(guī)方式
- 注意 RemoteCallbackListener 的使用,begin & finish 成對(duì)使用
- 可以使用權(quán)限或者包名的方式忠寻,在 onTransact 或者 onBind 方法中校驗(yàn)連接者
AIDL 代碼
// IBookManager.aidl
package com.test.testaidl_1;
import com.test.testaidl_1.Book;
import com.test.testaidl_1.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener l);
void unregisterListener(IOnNewBookArrivedListener l);
}
// Book.aidl.aidl
package com.test.testaidl_1;
parcelable Book;
服務(wù)端代碼
public class BookManagerService extends Service {
private static final String TAG = "bms";
// 線程安全的布爾對(duì)象
private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
// 線程安全的 List
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList
= new RemoteCallbackList<>();
@Nullable
@Override
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("ACCESS_BALABALA");
if (check == PackageManager.PERMISSION_DENIED) {
Log.e(TAG, "permission denied");
return null;
} else {
Log.e(TAG, "permission granted");
}
return mBinder;
}
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 l) throws RemoteException {
mListenerList.register(l);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Log.e(TAG, "register list size " + mListenerList.getRegisteredCallbackCount());
}
}
@Override
public void unregisterListener(IOnNewBookArrivedListener l) throws RemoteException {
mListenerList.unregister(l);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Log.e(TAG, "register list size " + mListenerList.getRegisteredCallbackCount());
}
}
};
@Override
public void onCreate() {
super.onCreate();
new Thread(new ServiceWorker()).start();
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestroyed.get()) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "new Book#" + bookId);
onNewBookArrived(newBook);
}
}
}
private void onNewBookArrived(Book newBook) {
// 運(yùn)行在 binder 線程池中
SystemClock.sleep(6000);
mBookList.add(newBook);
int size = mListenerList.beginBroadcast();
for (int i = 0; i < size; i++) {
try {
IOnNewBookArrivedListener broadcastItem = mListenerList.getBroadcastItem(i);
if (broadcastItem != null) {
// 客戶端回調(diào)惧浴,如果更新 UI,需要使用 handler 切換線程奕剃,否則會(huì)報(bào)錯(cuò)
broadcastItem.onNewBookArrived(newBook);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
mListenerList.finishBroadcast();
}
}
客戶端代碼
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
private IBookManager mBookManager;
private MyListener myListener = new MyListener();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConn, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> bookList = mBookManager.getBookList();
Log.e(TAG, "query book list, type is " + bookList.getClass().getCanonicalName());
Log.e(TAG, "query book list: " + Arrays.toString(bookList.toArray()));
mBookManager.addBook(new Book(3, "這是本新書"));
Log.e(TAG, "new book");
bookList = mBookManager.getBookList();
Log.e(TAG, "query book list: " + Arrays.toString(bookList.toArray()));
// 這是 UI 線程
mBookManager.registerListener(myListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
public void testBinder(View view) {
Toast.makeText(this,"999",Toast.LENGTH_SHORT).show();
// mBookManager.getBookList(); 模擬耗時(shí)操作
// 若不放在子線程中衷旅,就會(huì) ANR
new Thread(){
@Override
public void run() {
super.run();
try {
List<Book> bookList = mBookManager.getBookList();
Log.e(TAG, "query book list: " + Arrays.toString(bookList.toArray()));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}.start();
}
private class MyListener extends IOnNewBookArrivedListener.Stub {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
// 此方法運(yùn)行在 Binder 線程的線程池中
// 如果在這里做操作,就會(huì)拋異常纵朋,不是在 Looper 線程
Toast.makeText(MainActivity.this,
"666",Toast.LENGTH_SHORT).show();
mHandler.obtainMessage(-1, book).sendToTarget();
}
}
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == -1) {
Log.e(TAG, "收到新書:" + msg.obj.toString());
}
}
};
@Override
protected void onDestroy() {
super.onDestroy();
if (mBookManager != null && mBookManager.asBinder().isBinderAlive()) {
try {
mBookManager.unregisterListener(myListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConn);
}
}
以這個(gè) AIDL 實(shí)現(xiàn)為例柿顶,簡單分析一下代碼走向。
客戶端發(fā)起綁定遠(yuǎn)程服務(wù)的請(qǐng)求倡蝙,經(jīng)過源碼走向九串,會(huì)走向指定的服務(wù)信息;
-
服務(wù)或者為本地服務(wù)寺鸥,或者為其他應(yīng)用(遠(yuǎn)端服務(wù)),都會(huì)通過 onBind 方法將實(shí)例化好的 Binder 對(duì)象返回給客戶端品山;
// Stub extends Binder private Binder mBinder = new IBookManager.Stub() {}
-
然后代碼就會(huì)走到客戶端的 onServiceConnected 回調(diào):
private ServiceConnection mConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mBookManager = IBookManager.Stub.asInterface(service); } }
具體我們來看一下系統(tǒng)自動(dòng)生成的 IBookManager 的 java 類:
public interface IBookManager extends android.os.IInterface { public static abstract class Stub extends Binder implements IBookManager { public Stub() { this.attachInterface(this, DESCRIPTOR); } public static IBookManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof IBookManager))) { return ((IBookManager) iin); } return new IBookManager.Stub.Proxy(obj); } @Override public boolean onTransact(... ...) { switch (code) { case TRANSACTION_getBookList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.ljt.testaidl_1.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.ljt.testaidl_1.IBookManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public java.util.List<com.ljt.testaidl_1.Book> getBookList() { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); java.util.List<com.ljt.testaidl_1.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.ljt.testaidl_1.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_getBookList = ... ...; } public java.util.List<com.ljt.testaidl_1.Book> getBookList(); }
Stub 類實(shí)際上就是個(gè) Binder胆建,用來進(jìn)行序列化、反序列化操作肘交;Proxy 是服務(wù)端在客戶端的遠(yuǎn)程代理笆载,當(dāng)服務(wù)端不在本地時(shí),才會(huì)使用到涯呻。
-
如果服務(wù)在本地凉驻,那在 service connect 的時(shí)候,Binder 就會(huì)找到 aidl 對(duì)應(yīng)的本地實(shí)現(xiàn)复罐,并將該對(duì)象返回回去涝登;
如果服務(wù)不在本地,就會(huì)使用代理對(duì)象:
public static IBookManager asInterface(android.os.IBinder obj) { android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof IBookManager))) { return ((IBookManager) iin); } return new IBookManager.Stub.Proxy(obj); }
如果服務(wù)在本地效诅,那之后的交互就很簡單了胀滚,因?yàn)閷?duì)應(yīng)的實(shí)現(xiàn)類已經(jīng)找到,直接用引用進(jìn)行調(diào)用即可乱投,涉及不到 binder 通信咽笼;
-
如果服務(wù)在遠(yuǎn)端,當(dāng)用戶發(fā)起方法調(diào)用時(shí)戚炫,會(huì)進(jìn)入到 Proxy 的方法中:
public java.util.List<com.ljt.testaidl_1.Book> getBookList() { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); java.util.List<com.ljt.testaidl_1.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.ljt.testaidl_1.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; }
根據(jù)代碼來看剑刑,當(dāng)客戶端發(fā)起調(diào)用時(shí),會(huì)先獲取兩個(gè) Parcel 對(duì)象双肤,并且通過 transact 方法傳遞給服務(wù)端施掏。
同時(shí)钮惠,客戶端線程掛起,等待服務(wù)端返回?cái)?shù)據(jù)其监;
public final boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { boolean r = onTransact(code, data, reply, flags); return r; }
根據(jù) Binder 源碼可知萌腿,會(huì)回調(diào)到 onTransact 方法,也就是 Proxy 的 onTransact 方法:
@Override public boolean onTransact(... ...) { switch (code) { case TRANSACTION_getBookList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.ljt.testaidl_1.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } } return super.onTransact(code, data, reply, flags); }
經(jīng)過 onTransact 方法的處理抖苦,會(huì)像 reply 的 parcel 對(duì)象中寫入返回值毁菱,之后該方法返回 true 喚醒客戶端,繼續(xù)執(zhí)行锌历,從而完成整個(gè)調(diào)用贮庞。
以上,大概就是使用 AIDL 進(jìn)行 binder 通信的過程究西。