程序間通訊學習系列(二)——簡單瞭解Binder機制
阿新 • • 發佈:2019-01-29
Binder機制太複雜了,本文只是簡單的對Binder進行了解。
在Android中Binder是一個類,實現了IBinder介面,在Binder機制中還有兩個重要角色Binder驅動(在核心中)和ServiceManager,這兩部分Android平臺已經實現,我們不必關心,當然有興趣的童鞋可以研究下。本文主要學習瞭解應用層的Client和Service相關的Binder知識。
Messenge,ContentProvider等底層實現都是AIDL,所以以AIDL的簡單實現來學習Binder機制。
需求:圖書管理系統中,1.需要查詢圖書列表;2.新增新書到圖書庫中。
先看一下目錄結構:
首先新建一個Book
新建IBookManager.aidl檔案,生命兩個介面:public class Book implements Parcelable { private double price; private long bookId; private String bookName; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeDouble(this.price); dest.writeLong(this.bookId); dest.writeString(this.bookName); } public Book() { } protected Book(Parcel in) { this.price = in.readDouble(); this.bookId = in.readLong(); this.bookName = in.readString(); } 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]; } }; }
package anyan.com.ipcdemo.aboutaidl;
//注意匯入Book類(手動匯入)
import anyan.com.ipcdemo.aboutaidl.Book;
interface IBookManager {
List<Book> getBookList();//查詢圖書列表
void addBook(in Book book);//新增新圖書
}
因為在IBookManager.aidl檔案中使用了非基本資料型別,所以Book類不僅要實現序列化介面,還要新建一個Book.aidl檔案(至於為什麼,我也不知道),注意這個Book.aidl檔案的全路徑包名要與Book.java的全路徑包名一致,否則編譯會報錯。內容如下:
package anyan.com.ipcdemo.aboutaidl;
parcelable Book;
Rebuild project成功後再app/build/generated/source/aidl/debug/包名 目錄下回生成一個IBookManager.java檔案,這是AS幫我們自動生成的,這裡使用了代理模式(可簡單瞭解一下,Java設計模式-代理模式實現及原理)主要分析這裡的程式碼:包含兩個介面getBookList()和addBook(),可以看到這兩個介面就是我們在IBookManager.aidl檔案中所宣告的。一個內部類Stub,繼承Binder實現IBookManager介面。Stub類裡面還有一個內部類Proxy也實現了IBookManager介面,這個就是Stub類(也就是Binder)的代理類。
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements anyan.com.ipcdemo.aboutaidl.IBookManager {
//Binder的唯一標識,一般用類名錶示
private static final java.lang.String DESCRIPTOR = "anyan.com.ipcdemo.aboutaidl.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an anyan.com.ipcdemo.aboutaidl.IBookManager interface,
* generating a proxy if needed.
* 將IBinder物件轉換成IBookManager介面並返回
* 這種轉換區分程序,如果是在同一程序,直接返回服務端的Stub物件;
* 如果不在同一程序,則返回代理物件Stub.Proxy;
*/
public static anyan.com.ipcdemo.aboutaidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof anyan.com.ipcdemo.aboutaidl.IBookManager))) {
return ((anyan.com.ipcdemo.aboutaidl.IBookManager) iin);
}
return new anyan.com.ipcdemo.aboutaidl.IBookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 該方法在服務端的Binder執行緒池中執行,注意採用同步方法
* @param code 確定客戶端請求的目標方法是哪個
* @param data 目標方法所需引數資料
* @param reply 目標方法返回資料
* @param flags
* @return
* @throws android.os.RemoteException
*/
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<anyan.com.ipcdemo.aboutaidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
anyan.com.ipcdemo.aboutaidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = anyan.com.ipcdemo.aboutaidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//代理物件
private static class Proxy implements anyan.com.ipcdemo.aboutaidl.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;
}
/**
* 該方法在客戶端執行,
* @return
* @throws android.os.RemoteException
*/
@Override
public java.util.List<anyan.com.ipcdemo.aboutaidl.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<anyan.com.ipcdemo.aboutaidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//呼叫遠端請求,當前執行緒掛起,服務端OnTransact()方法會被呼叫,
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
//當前執行緒繼續執行,取出結果
_reply.readException();
_result = _reply.createTypedArrayList(anyan.com.ipcdemo.aboutaidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(anyan.com.ipcdemo.aboutaidl.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();
}
}
}
//方法的id
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<anyan.com.ipcdemo.aboutaidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(anyan.com.ipcdemo.aboutaidl.Book book) throws android.os.RemoteException;
}
如下圖所示:
接著看具體怎麼使用,建立一個BookManagerService類:
public class BookService extends Service {
public BookService() {
}
//支援併發讀寫
private CopyOnWriteArrayList<Book> bookList = new CopyOnWriteArrayList<>();
private IBookManager.Stub binder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return bookList;
}
@Override
public void addBook(Book book) throws RemoteException {
bookList.add(book);
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
在Activity中BindService:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private IBookManager iBookManager;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//用到了上面提到了轉換方法asInterface,將IBinder轉換為介面,
//同一程序直接返回Stub物件,不同程序返回Proxy代理類
iBookManager = IBookManager.Stub.asInterface(service);
try {
service.linkToDeath(deathRecipient, 0);
//拿到物件後就可以直接呼叫方法了
List<Book> bookList = iBookManager.getBookList();
Log.e(TAG, "onServiceConnected: " + bookList);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
//死亡代理,binder死亡時會收到通知,回撥binderDied方法
private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (iBookManager == null)
return;
iBookManager.asBinder().unlinkToDeath(deathRecipient, 0);
iBookManager = null;
//重新繫結service
Intent intent = new Intent(MainActivity.this, BookService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClick(View view) {
Intent intent = new Intent(this, BookService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
}
至此,簡單的Binder機制分析完了,看著可能有點懵,很正常,我是看了好幾篇才只是簡單的理解了。Fighting !!!