不借助AIDL實現Binder
- 第一步 聲明接口
/**
* 聲明一個接口悍手,該接口繼承IInterface
* IInterface代表的就是遠程server對象
* 在接口中聲明需要實現的方法,這些方法將在server中實現微饥,在client中被調用
*/
public interface IBookManager extends IInterface {
void addBook(Book book)throws android.os.RemoteException;
List<Book> getBookList()throws android.os.RemoteException;
}
- 第二步 實現可跨進程的類
/**
* 創(chuàng)建類BookManagerImp,繼承Binder類格侯,實現 IBookManager接口吁朦,然后創(chuàng)建內部類Proxy同樣實現IBookManager接口
* Binder類,代表的其實就是Binder本地對象加勤。BinderProxy類是Binder類的內部類,它代表遠程進程Binder對象的本地代理同波;
* IBinder是一個接口鳄梅,它代表了一種跨進程傳輸的能力;只要實現了這個接口未檩,就能將這個對象進行跨進程傳遞戴尸;這是驅動底層支持的;
* 在跨進程數據流驅動的時候冤狡,驅動會識別IBinder類型的數據孙蒙,從而自動完成不同進程Binder本地對象及Binder代理對象的轉換
*/
public abstract class BookManagerImp extends Binder implements IBookManager {
private static final java.lang.String DESCRIPTOR = "liuhe.com.ipcdemo.BookManagerImp";
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public BookManagerImp() {
/**
Convenience method for associating a specific interface with the Binder.
After calling, queryLocalInterface() will be implemented for you
to return the given owner IInterface when the corresponding
descriptor is requested
將特定接口與Binder相關聯的便捷方法。調用后悲雳,將實現queryLocalInterface()挎峦,
以便在請求相應的描述符時返回給定的所有者IInterface
*/
this.attachInterface(this, DESCRIPTOR);
}
public static IBookManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
/**
Attempt to retrieve a local implementation of an interface
for this Binder object. If null is returned, you will need
to instantiate a proxy class to marshall calls through
the transact() method.
嘗試檢索此Binder對象的接口的本地實現。
如果返回null合瓢,則需要實例化代理類以通過transact()方法編組調用坦胶。
*/
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof IBookManager) {
return (IBookManager) iin;
} else {
return new Proxy(obj);
}
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
liuhe.com.ipcdemo.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = liuhe.com.ipcdemo.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<liuhe.com.ipcdemo.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IBookManager {
private IBinder mRemote;
public Proxy(IBinder mRemote) {
this.mRemote = mRemote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void addBook(liuhe.com.ipcdemo.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);
}
/**
Perform a generic operation with the object.
@param code The action to perform. This should
be a number between {@link #FIRST_CALL_TRANSACTION} and
{@link #LAST_CALL_TRANSACTION}.
@param data Marshalled data to send to the target. Must not be null.
If you are not sending any data, you must create an empty Parcel
that is given here.
@param reply Marshalled data to be received from the target. May be
null if you are not interested in the return value.
@param flags Additional operation flags. Either 0 for a normal
RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
@return Returns the result from {@link Binder#onTransact}. A successful call
generally returns true; false generally means the transaction code was not
understood.
對對象執(zhí)行泛型操作。
@param code要執(zhí)行的操作晴楔。
這應該是{@link #FIRST_CALL_TRANSACTION}和{@link #LAST_CALL_TRANSACTION}之間的數字迁央。
@param _data 要發(fā)送到目標的整理后的數據。不能為空滥崩。如果您未發(fā)送任何數據,則必須創(chuàng)建此處給出的空包讹语。
@param _reply 要從目標接收的整理后數據钙皮。如果您對返回值不感興趣,則可以為null。
@param標志 附加操作標志短条。正常RPC為0导匣,單向RPC為{@link #FLAG_ONEWAY}。
@return返回{@link Binder#onTransact}的結果茸时。成功的通話通常會返回true; false通常表示不理解事務代碼贡定。
*/
mRemote.transact(TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.util.List<liuhe.com.ipcdemo.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<liuhe.com.ipcdemo.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(liuhe.com.ipcdemo.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
@Override
public IBinder asBinder() {
return this;
}
}
- 定義一個service并實現server功能
public class BinderService extends Service {
private static final String TAG = "BinderService";
ArrayList<Book> books;
IBinder iBinder = new BookManagerImp() {
@Override
public void addBook(Book book) throws RemoteException {
Log.i(TAG, "addBook");
books.add(book);
}
@Override
public List<Book> getBookList() throws RemoteException {
Log.i(TAG, "getBookList" + books.toString());
return books;
}
};
@Override
public IBinder onBind(Intent intent) {
books = new ArrayList();
return iBinder;
}
}
- 在Activity中啟動service,調用server的方法
public class MainActivity extends AppCompatActivity {
int index = 1;
IBookManager bookManager;
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.textView);
findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (bookManager != null) {
Book book = new Book("Book" + index);
index++;
try {
//調用server端addBook方法
bookManager.addBook(book);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
//使用bindService方式啟動service
final Intent intent = new Intent(getApplicationContext(), BinderService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
}
}
});
findViewById(R.id.get_book_list).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//調用server端getBookList方法浓体,獲取返回數據并顯示
tv.setText(bookManager.getBookList().toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Toast.makeText(getApplicationContext(), "success", Toast.LENGTH_LONG).show();
bookManager = BookManagerImp.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
bookManager = null;
}
};
}
ok了备恤,這樣我們就實現了跨進程通信棍丐。
執(zhí)行過程分析
我們先看一下service是怎么定義的
<service
android:name=".BinderService"
android:process=":remote"></service>
我們知道只要給service增加了process屬性,那么它就會運行在這個指定的remote進程中旋炒。如果我們要調試程序,IDE就會讓我們選擇要調試的進程签杈,如下圖所示:
我們先把process屬性去掉瘫镇,看一下在同一個進程中代碼是怎么執(zhí)行的。
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Toast.makeText(getApplicationContext(), "success", Toast.LENGTH_LONG).show();
bookManager = BookManagerImp.asInterface(iBinder);
}
當bindService成功之后會返回一個IBinder對象答姥,我們將這個對象當做參數傳到BookManagerImp的asInterface方法中铣除,然后獲取了一個IBookManager對象。來我們看一下慢動作鹦付,debug走起~
我們可以看到IBinder對象通過調用queryLocalInterface方法返回了一個IInterface對象尚粘,該對象不為空并且等于IBookManager ,直接將該對象轉換為IBookManager睁壁,作為結果返回背苦。
我們看一下queryLocalInterface的注釋:
/**
Attempt to retrieve a local implementation of an interface
for this Binder object. If null is returned, you will need
to instantiate a proxy class to marshall calls through
the transact() method.
嘗試檢索此Binder對象的接口的本地實現。
如果返回null潘明,則需要實例化代理類以通過transact()方法編組調用行剂。
*/
注釋中說明該方法返回的IInterface是一個本地Binder對象實現,不需要跨進程通信钳降。我們繼續(xù)調試就會發(fā)現在MainActivity中bookManager可以直接調用server中的方法厚宰,不會再走BookManagerImp的其他方法。
好了遂填,分析完在相同進程的通信過程铲觉,我們再來看看跨進程的過程,把android:process=":remote"再個service加上吓坚,走起撵幽。
和之前的步驟一樣,運行調試礁击,走到asInterface方法中
我們看到iin為空盐杂,最終返回了一個Proxy對象逗载。接下來在MainActivity中調用addBook方法,我們看到系統會調用Proxy的addBook方法链烈,如下圖所示:
可以看到在Proxy的addBook方法中調用了mRemote的transact的方法厉斟,并將方法的code值及構造的序列化的參數傳遞進去。我們看一下transact的注釋:
/**@return Returns the result from {@link Binder#onTransact}. A successful call
* generally returns true; false generally means the transaction code was not understood.
*@return返回{@link Binder#onTransact}的結果强衡。成功的通話通常會返回true; false通常表示不理解事務代碼擦秽。
*/
很明顯,我們傳過去的code值和參數最終會交給 Binder的onTransact方法漩勤,也就是BookManagerImp的onTransact方法感挥。我們可以推測,我們把斷點放在下圖所示的地方應該是可以看到接下來的執(zhí)行過程的锯七。
皮皮蝦我們走~
走啊链快,走啊,走啊眉尸,怎么不走S蛭稀!T牖霉祸??袱蜡?
可以思考一下為什么斷點沒走到這丝蹭?
我們選擇調試模式的時候IDE會讓我們選擇調試的進程,我們一開始選擇的是主進程坪蚁,現在BookManagerImp是在remote進程中奔穿,所以斷點是無法到達的。好了敏晤,我們重新選擇調試進程試試:
果然如此贱田,斷點成功進入!
我們可以看到code值為1嘴脾,所以接下來會走case TRANSACTION_addBook: 最終會調用IBookManager的addBook方法男摧,也就是service中的addBook方法的實現會被調用。
就這樣译打,跨進程通信成功了耗拓!
如果有問題歡迎在評論區(qū)留言,一起交流學習奏司!
參考:
任玉剛《Android 開發(fā)藝術探索》
weishu《Binder學習指南》