1.具體代碼:
服務端實現:
public class IPCService extends Service {
private static final String DESCRIPTOR = "IPCService";
private final String[] names = { "B神", "艹神", "基神", "J神", "翔神" };
private MyBinder mBinder = new MyBinder();
private class MyBinder extends Binder {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case 0x001: {
data.enforceInterface(DESCRIPTOR);
int num = data.readInt();
reply.writeNoException();
reply.writeString(names[num]);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
可以看到onTransact有四個參數
code , data ,replay , flags
- code 是一個整形的唯一標識,用於區分執行哪個方法,客戶端會傳遞此參數,告訴服務端執行哪個方法;
- data客戶端傳遞過來的參數;
- replay服務器返回回去的值;
- flags標明是否有返回值,0為有(雙向),1為沒有(單向)。
客戶端實現:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText edit_num;
private Button btn_query;
private TextView txt_result;
private IBinder mIBinder;
private ServiceConnection PersonConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mIBinder = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBinder = service;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindViews();
// 綁定遠程Service
Intent service = new Intent("android.intent.action.IPCService");
service.setPackage("com.jay.ipcserver");
bindService(service, PersonConnection, BIND_AUTO_CREATE);
btn_query.setOnClickListener(this);
}
private void bindViews() {
edit_num = (EditText) findViewById(R.id.edit_num);
btn_query = (Button) findViewById(R.id.btn_query);
txt_result = (TextView) findViewById(R.id.txt_result);
}
@Override
public void onClick(View v) {
int num = Integer.parseInt(edit_num.getText().toString());
if (mIBinder == null) {
Toast.makeText(this, "未連接服務端或服務端被異常殺死", Toast.LENGTH_SHORT).show();
} else {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
String _result = null;
try {
_data.writeInterfaceToken("IPCService");
_data.writeInt(num);
mIBinder.transact(0x001, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
txt_result.setText(_result);
edit_num.setText("");
} catch (RemoteException e) {
e.printStackTrace();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
}
2.transact和onTransact的區別
談transact 和onTransact需要先聊聊iBinder
IBinder是什么呢?首先要明白,Android的遠程調用(就是跨進程調用)就是通過IBinder實現的,下面是對android開發文檔的翻譯。
IBinder是遠程對象的基本接口,是為高性能而設計的輕量級遠程調用機制的核心部分。但它不僅用於遠程調用,也用於進程內調用。這個接口定義了與遠程對象交互的協議。不要直接實現這個接口,而應該從Binder派生。
IBinder的主要API是transact(),與它對應另一方法是Binder.onTransact()。第一個方法使你可以向遠端的IBinder對象發送發出調用,第二個方法使你自己的遠程對象能夠響應接收到的調用。IBinder的API都是同步執行的,比如transact()直到對方的Binder.onTransact()方法調用完成后才返回。調用發生在進程內時無疑是這樣的,而在進程間時,在IPC的幫助下,也是同樣的效果。
通過transact()發送的數據是Parcel,Parcel是一種一般的緩沖區,除了有數據外還帶有一些描述它內容的元數據。元數據用於管理IBinder對象的引用,這樣就能在緩沖區從一個進程移動到另一個進程時保存這些引用。這樣就保證了當一個IBinder被寫入到Parcel並發送到另一個進程中,如果另一個進程把同一個IBinder的引用回發到原來的進程,那么這個原來的進程就能接收到發出的那個IBinder的引用。這種機制使IBinder和Binder像唯一標志符那樣在進程間管理。
系統為每個進程維護一個存放交互線程的線程池。這些交互線程用於派送所有從另外進程發來的IPC調用。例如:當一個IPC從進程A發到進程B,A中那個發出調用的線程(這個應該不在線程池中)就阻塞在transact()中了。進程B中的交互線程池中的一個線程接收了這個調用,它調用Binder.onTransact(),完成后用一個Parcel來做為結果返回。然后進程A中的那個等待的線程在收到返回的Parcel后得以繼續執行。實際上,另一個進程看起來就像是當前進程的一個線程,但不是當前進程創建的。
Binder機制還支持進程間的遞歸調用。例如,進程A執行自己的IBinder的transact()調用進程B的Binder,而進程B在其Binder.onTransact()中又用transact()向進程A發起調用,那么進程A在等待它發出的調用返回的同時,還會用Binder.onTransact()響應進程B的transact()。總之Binder造成的結果就是讓我們感覺到跨進程的調用與進程內的調用沒什么區別。
當操作遠程對象時,你經常需要查看它們是否有效,有三種方法可以使用:
1 transact()方法將在IBinder所在的進程不存在時拋出RemoteException異常。
2 如果目標進程不存在,那么調用pingBinder()時返回false。
3 可以用linkToDeath()方法向IBinder注冊一個IBinder.DeathRecipient,在IBinder代表的進程退出時被調用。
要實現IBinder來支持遠程調用,應從Binder類派生一個類。Binder實現了IBinder接口。但是一般不需要直接實現此類,而是跟據你的需要由開發包中的工具生成,這個工具叫AIDL。你通過AIDL語言定義遠程對象的方法,然后用AIDL工具生成Binder的派生類,然后就可使用之。然而,可是,但是,當然,你也可以直接從Binder類派生以實現自定義的RPC調用,或只是實例化一個原始的Binder對象直接作為進程間共享的令牌來使用。
