AIDL( Android Interface Definition Language),安卓接口定義語(yǔ)言齐疙,用于定義服務(wù)器和客戶端通信接口的一種描述語(yǔ)言,主要是解決了android進(jìn)程間通信的問(wèn)題艇棕。
在AIDL中咽瓷,不是所有的數(shù)據(jù)類型都是可以使用的∶辰郑可用類型如下:
- 基本數(shù)據(jù)類型(byte char int long boolean float double)庵寞,short不支持
- String和CharSequence
- List:只支持ArrayList,里面的每個(gè)元素都必須被AIDL支持
- Map:只支持HashMap薛匪,里面的每個(gè)key皇帮,value都必須被AIDL支持
- Parcelable:所有實(shí)現(xiàn)了Parcelable接口的對(duì)象
- AIDL 所有的AIDL接口本身也可以在AIDL文件中使用
需要注意的是: - AIDL中自定義的Parcelable對(duì)象和AIDL對(duì)象必須要實(shí)現(xiàn)import進(jìn)來(lái),即使是在同一個(gè)包下面蛋辈。
- 如果AIDL中用到了Parcelable對(duì)象属拾,那么必須要新建一個(gè)和它同名的AIDL文件,并在其中聲明它為Parcelable對(duì)象冷溶。
- AIDL中除了基本數(shù)據(jù)渐白,其他類型的參數(shù)必須標(biāo)上方向:in、out逞频、inout纯衍,in表示輸入型參數(shù),out表示輸出型參數(shù)苗胀,inout表示輸入輸出型參數(shù)襟诸。
接下來(lái)通過(guò)Demo來(lái)講解:
//自定義數(shù)據(jù)類型Book瓦堵,實(shí)現(xiàn)Parcelable接口完成序列化
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
}
接著New->AIDL->AIDL file創(chuàng)建AIDL文件,注意要明確導(dǎo)包
// Book.aidl 歌亲,可能會(huì)出現(xiàn)同名問(wèn)題菇用,可以先建一個(gè)別的名字再改回來(lái)
package com.wtwd.aidl;
parcelable Book;
// IBookManager.aidl
package com.wtwd.aidl;
import com.wtwd.aidl.Book;
import com.wtwd.aidl.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
// IOnNewBookArrivedListener.aidl
package com.wtwd.aidl;
import com.wtwd.aidl.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newbook);
}
接著build一下項(xiàng)目會(huì)在app/build/generated/aidl_source_output_dir/debug/compileDebugAidl/out目錄下找到對(duì)應(yīng)的AIDL生成文件(具體分析見(jiàn)Android中的IPC機(jī)制一文),接下來(lái)創(chuàng)建BookManagerService作為服務(wù)端核心代碼陷揪。
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
//CopyOnWriteArrayList是為了處理線程同步惋鸥,但是這里就和之前說(shuō)的AIDL只能夠使用ArrayList,為什么這兒可以
//使用CopyOnWriteArrayList呢悍缠?首先CopyOnWriteArrayList不是繼承自ArrayList的卦绣,且AIDL支持的是抽象的
//List,而List是一個(gè)接口飞蚓,雖然服務(wù)端返回的是CopyOnWriteArrayList滤港,但是Binder中會(huì)按照List的規(guī)范去訪
//問(wèn)數(shù)據(jù)并最終形成一個(gè)新的ArrayList給客戶端,同理ConcurrentHashMap也是如此趴拧。
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
//private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();
//Binder從客戶端傳過(guò)來(lái)的對(duì)象經(jīng)過(guò)反序列化后和原先是不同的溅漾,所以需要采用RemoteCallbackList來(lái)實(shí)現(xiàn)
//RemoteCallbackList是系統(tǒng)專門用來(lái)刪除跨進(jìn)程的listener接口,它是一個(gè)泛型八堡,支持管理任意的AIDL接口樟凄,這是因?yàn)樗訟IDL接口繼承自IInterface
//public class RemoteCallbackList<E extends IInterface> {}
//內(nèi)部采用Map結(jié)構(gòu)保存回調(diào),key是IBinder類型兄渺,value是CallBack類型
//ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();
//在CallBack中封裝了真正的遠(yuǎn)程listener缝龄,客戶端注冊(cè)listener的時(shí)候會(huì)把這個(gè)listener信息存到mCallbacks,key和value通過(guò)如下獲取
//IBinder binder = callback.asBinder();
//Callback cb = new Callback(callback, cookie);
//RemoteCallbackList內(nèi)部已實(shí)現(xiàn)線程同步
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
//用來(lái)記錄Service是否銷毀的標(biāo)志位挂谍,默認(rèn)false
private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean();
//服務(wù)端的遠(yuǎn)程方法
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
// if (!mListenerList.contains(listener)) {
// mListenerList.add(listener);
// } else {
// Log.e(TAG,"already exists.");
// }
// Log.e(TAG,"registerListener,size:"+mListenerList.size());
mListenerList.register(listener);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
// if (!mListenerList.contains(listener)) {
// mListenerList.remove(listener);
// Log.e(TAG,"unregister listener success");
// } else {
// Log.e(TAG,"not found,can not unregister.");
// }
// Log.e(TAG,"unregisterListener,size:"+mListenerList.size());
mListenerList.unregister(listener);
}
};
//服務(wù)端返回的IBinder對(duì)象
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1,"Android"));
mBookList.add(new Book(2,"Ios"));
//開(kāi)啟子線程叔壤,執(zhí)行任務(wù)
new Thread(new ServiceWorker()).start();
}
@Override
public void onDestroy() {
mIsServiceDestroyed.set(true);
super.onDestroy();
}
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
// Log.e(TAG,"onNewBookArrived,notify listener:"+mListenerList.size());
for (int i=0;i<N;i++) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
Log.e(TAG,"onNewBookArrived,notify listener:"+listener);
if (listener != null) {
listener.onNewBookArrived(book);
}
}
mListenerList.finishBroadcast();
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestroyed.get()) {
try {
//每隔5s執(zhí)行任務(wù),回調(diào)給客戶端
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size()+1;
Book newBook = new Book(bookId,"new book#"+bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
以上就是AIDL服務(wù)端的代碼了口叙,雖然看起來(lái)比較復(fù)雜炼绘,但是細(xì)細(xì)評(píng)味下來(lái)還是獲益匪淺的。接下來(lái)就開(kāi)始客戶端的代碼編寫(xiě)了妄田,通常我們接觸最多的也是這方面俺亮,服務(wù)端會(huì)把相關(guān)的AIDL代碼提供過(guò)來(lái),我們直接把它復(fù)制粘貼到項(xiàng)目中就可以了疟呐,需要注意一點(diǎn)的是,如果存在自定義數(shù)據(jù)類型脚曾,需要將JavaBean放到j(luò)ava目錄和AIDL相同的包下面,如下所示:
接下來(lái)Build一下Project就可以生成對(duì)應(yīng)的AIDL接口文件了
public class MainActivity extends AppCompatActivity {
private static final String TAG = "kris_ni";
private static final int MESSAGE_NEW_BOOK_ARRIVED = 0x1;
private IBookManager iBookManager;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.e(TAG,"receive new book :" + msg.obj);
break;
default:
break;
}
}
};
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
iBookManager = IBookManager.Stub.asInterface(iBinder);
try {
List<Book> bookList = iBookManager.getBookList();
Log.e(TAG,"query book list,list type: "+ bookList.getClass().getCanonicalName());
Log.e(TAG,"query book list: "+bookList.toString());
Book newBook = new Book(3,"Kotlin");
iBookManager.addBook(newBook);
Log.e(TAG,"add new book: "+newBook);
List<Book> newList = iBookManager.getBookList();
Log.e(TAG,"query book list: "+newList.toString());
iBookManager.registerListener(listener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
iBookManager = null;
Log.e(TAG,"binder died");
}
};
private IOnNewBookArrivedListener listener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,book).sendToTarget();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent("com.example.aidldemo.BookManagerService");
intent.setPackage("com.example.aidldemo");
bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if (iBookManager != null && iBookManager.asBinder().isBinderAlive()) {
try {
iBookManager.unregisterListener(listener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(serviceConnection);
super.onDestroy();
}
}
總體看下來(lái)AIDL也就這樣了么启具,當(dāng)然不止這些本讥。有沒(méi)有考慮過(guò)AIDL被濫用的情況呢?可以通過(guò)權(quán)限來(lái)限制客戶端,簡(jiǎn)單的寫(xiě)法就是在服務(wù)端聲明權(quán)限
<permission android:name="com.example.aidldemo.permission.ACCESS_BOOK_SERVICE"
android:protectionLevel="normal"/>
<uses-permission android:name="com.example.aidldemo.permission.ACCESS_BOOK_SERVICE"/>
接著就可以在Service里面判斷是否將IBinder返回了
@Override
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("com.example.aidldemo.permission.ACCESS_BOOK_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
Log.e(TAG,"has no permission");
return null;
}
return mBinder;
}
客戶端所做的是需要申明權(quán)限才可以進(jìn)行ServiceConnection
<uses-permission android:name="com.example.aidldemo.permission.ACCESS_BOOK_SERVICE"/>
另外一種寫(xiě)法如下:
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
int check = checkCallingOrSelfPermission("com.example.aidldemo.permission.ACCESS_BOOK_SERVICE");
Log.d(TAG, "check=" + check);
if (check == PackageManager.PERMISSION_DENIED) {
return false;
}
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(
getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
Log.d(TAG, "onTransact: " + packageName);
if (!packageName.startsWith("com.example")) {
return false;
}
return super.onTransact(code, data, reply, flags);
}
AIDL的介紹到此就差不多結(jié)束了拷沸,有沒(méi)有思考過(guò)Binder可能會(huì)意外被銷毀呢色查?在接下來(lái)的Binder連接池里面會(huì)重點(diǎn)為大家介紹,感謝您的閱讀撞芍,記得隨手點(diǎn)個(gè)贊喔秧了!