本文參考自Android開發藝術探索第二章,AIDL(Android接口定義語言)是Android提供的一種進程間通訊機制,咱們能夠利用它定義客戶端與服務端相互通訊都承認的編程接口。先來看看AIDL的用法,而後經過源碼理解下AIDL生成的java類文件java
下面我以一個簡單的跨進程進行添加Book,查看BookList。這裏使用兩個App爲例,首先編寫一個Book類該類實現了Parcelable接口,代碼以下android
public class Book implements Parcelable {
public int bookId;
public String bookName;
private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public Book(int id, String name) {
bookId = id;
bookName = name;
}
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);
}
}
複製代碼
而後在新建一個Book.aidl,IBookManager.aidl源碼以下編程
// Book.aidl
parcelable Book;
// IBookManager.aidl
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
複製代碼
而後把Book.java、Book.aidl、IBookManager.aidl分別複製到client、server兩個module,接着在server這個module裏面新建一個BookService,再在清單文件中配置IntentFilter,代碼以下數組
class BookService : Service() {
// 因爲addBook方法運行在Binder線程中併發調用可能會出問題所以使用CopyOnWriteArrayList
private val mBookList = CopyOnWriteArrayList<Book>()
override fun onBind(intent: Intent?): IBinder? {
return object : IBookManager.Stub() {
override fun registerListener(listener: IOnNewBookArrivedListener?) {
// listener 爲IOnNewBookArrivedListener.Stub.Proxy實例
mListener.register(listener)
}
override fun unregisterListener(listener: IOnNewBookArrivedListener?) {
mListener.unregister(listener)
}
override fun getBookList(): MutableList<Book> {
return mBookList
}
override fun addBook(book: Book?) {
mBookList.add(book)
}
}
}
}
<service android:name=".BookService" android:exported="true" android:enabled="true">
<intent-filter>
<action android:name="com.hefuwei.testaidl.bookService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
複製代碼
最後在client這個module的MainActivity與BookService進行通訊,MainActivity代碼以下,須要注意的是Intent得設置將要啓動的應用程序的包名這裏就是Server這個module的包名併發
// 這裏不須要使用DeathRecipient由於onServiceConnected其實就是使用DeathRecipient實現的,二者
// 的區別是onServiceDisconnected運行在主線程中,而DeathRecipient.binderDied運行在Binder線程池中
class MainActivity : AppCompatActivity() {
private lateinit var mListener: IOnNewBookArrivedListener
private var mManager: IBookManager? = null
private var mBookID = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
mManager = null
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mManager = IBookManager.Stub.asInterface(service)
}
}
val intent = Intent()
intent.action = "com.hefuwei.aidl.BookService"
intent.setPackage(packageName)
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
}
fun addBook(v: View) {
mManager?.addBook(Book(++mBookID, "第${mBookID}本書"))
}
fun allBooks(v: View) {
Log.d("AIDL", mManager?.bookList?.toString())
}
override fun onDestroy() {
super.onDestroy()
unbindService(mConnection)
}
}
複製代碼
至此每當點擊Client就能夠與Server進行通訊了,來總結下用法ide
接下來看看系統爲咱們生成的IBookManager.java的源碼源碼分析
生成的代碼以下所示this
public interface IBookManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.hefuwei.testaidl.aidl.IBookManager {
// 1
private static final java.lang.String DESCRIPTOR = "com.hefuwei.testaidl.aidl.IBookManager";
// 2
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
// 3
public static com.hefuwei.testaidl.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.hefuwei.testaidl.aidl.IBookManager))) {
return ((com.hefuwei.testaidl.aidl.IBookManager) iin);
}
return new com.hefuwei.testaidl.aidl.IBookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
// 6
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
java.util.List<com.hefuwei.testaidl.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
com.hefuwei.testaidl.aidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.hefuwei.testaidl.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.hefuwei.testaidl.aidl.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;
}
@Override
public java.util.List<com.hefuwei.testaidl.aidl.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<com.hefuwei.testaidl.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.hefuwei.testaidl.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
// 4
@Override
public void addBook(com.hefuwei.testaidl.aidl.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();
}
}
}
// 7
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.hefuwei.testaidl.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.hefuwei.testaidl.aidl.Book book) throws android.os.RemoteException;
}
複製代碼
重點代碼解釋以下spa
上面只說到了客戶端調用服務端的方法,可是假設客戶端想要監聽服務端書籍數量的變化,一種作法是一直不停的輪詢去請求,另外一種作法是使用觀察者模式註冊監聽,這裏講講怎麼註冊觀察者,首先建立IOnNewBookArrivedListener.aidl文件,代碼以下線程
interface IOnNewBookArrivedListener {
void onBookArrived(in Book book);
}
複製代碼
而後修改下IBookManager.aidl添加註冊和解除註冊兩個方法
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
複製代碼
接着將這兩個aidl文件同步給Server和Client,而後修改Client的MainActivity的代碼
class MainActivity : AppCompatActivity() {
private lateinit var mConnection: ServiceConnection
private lateinit var mListener: IOnNewBookArrivedListener
private var mManager: IBookManager? = null
private var mBookID = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
mManager = null
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mManager = IBookManager.Stub.asInterface(service)
}
}
mListener = object : IOnNewBookArrivedListener.Stub() {
override fun onBookArrived(book: Book?) {
// 運行在客戶端的Binder線程池中
runOnUiThread {
Toast.makeText(this@MainActivity, "${book?.id}", Toast.LENGTH_SHORT).show()
}
}
}
val intent = Intent()
intent.action = "com.hefuwei.aidl.BookService"
intent.setPackage(packageName)
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
}
fun addBook(v: View) {
mManager?.addBook(Book(++mBookID, "第${mBookID}本書"))
}
fun allBooks(v: View) {
Log.d("AIDL", mManager?.bookList?.toString())
}
fun register(v: View) {
mManager?.registerListener(mListener)
Toast.makeText(this, "註冊", Toast.LENGTH_SHORT).show()
}
fun unregister(v: View) {
mManager?.unregisterListener(mListener)
Toast.makeText(this, "解除註冊", Toast.LENGTH_SHORT).show()
}
override fun onDestroy() {
super.onDestroy()
unbindService(mConnection)
}
}
複製代碼
最後修改下服務端的BookService代碼
class BookService : Service() {
private val mBookList = CopyOnWriteArrayList<Book>()
private val mListener = RemoteCallbackList<IOnNewBookArrivedListener>()
override fun onBind(intent: Intent?): IBinder? {
return object : IBookManager.Stub() {
override fun registerListener(listener: IOnNewBookArrivedListener?) {
// listener 爲IOnNewBookArrivedListener.Stub.Proxy實例
mListener.register(listener)
}
override fun unregisterListener(listener: IOnNewBookArrivedListener?) {
mListener.unregister(listener)
}
override fun getBookList(): MutableList<Book> {
return mBookList
}
override fun addBook(book: Book?) {
mBookList.add(book)
}
}
}
override fun onCreate() {
super.onCreate()
Executors.newScheduledThreadPool(1).scheduleWithFixedDelay({
val n = mListener.beginBroadcast()
for (i in 0 until n) {
mListener.getBroadcastItem(i).onBookArrived(Book(12, "12書"))
}
mListener.finishBroadcast()
}, 0, 3, TimeUnit.SECONDS)
}
}
複製代碼
注意這裏須要使用RemoteCallbackList來存儲listener,由於registerListener的入參是一個IOnNewBookArrivedListener.Stub.Proxy對象,每次IPC都會建立一個新的對象,這樣就無法解除註冊了。RemoteCallbackList內部維護了一個Key爲Binder實例的Map,雖然每次會建立一個新的Proxy對象,可是這個Proxy對象的mRemote成員一直是同一個所以能夠進行區分。而當服務端的數據發送變化後就能夠從RemoteCallbackList取出Proxy對象調用客戶端的方法
注意:若是經過AIDL傳遞一個Binder對象會先調用Parcel.writeStrongBinder將Binder寫入,而後在目標線程取出時會調用Parcel.readStrongBinder將Binder讀出,若是寫入端和讀出端位於兩個進程,讀取到的時候會是一個BinderProxy對象而不是原先的Binder對象