在Android中,当两个类都在同一个进程里执行时,两者之间的沟通,只要采取一般的函数调用(Function Call)就行了,既快速又方便。一旦两个类分别在不同的进程里执行时,两者之间的沟通,就不能采取一般的函数调用途径了。只好采取IPC沟通途径。 Android框架的IPC沟通仰赖单一的IBinder接口。此时Client端调用IBinder接口的transact()函数,透过IPC机制而调用到远方(Remote)的onTransact()函数。此时的onTransact()函数是Binder类中的函数,之所以transact()能执行到远方的onTransact(),原因是因为Binder实现了IBinder接口,Binder类复写的transact()函数内部调用了自己定义的onTransact()函数。 下面不用aidl,纯Binder来实现一次进程间的通信。
服务端实现:
首先定义一个接口:
接口定义两个方法,加法和减法。
public interface IEthan {
int getAdd(int i,int j);
int getminus(int i,int j);
}
下面是主角MyBinder类
public class MyBinder extends Binder implements IEthan {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {//code为客户端Transact发送过来的code
case 0:
int ii[] = { 0, 0 };//此处数组长度必须和客户端发来的数组长度一致,不然报错
data.readIntArray(ii);//获取从客户端发来的int数组,对应的getAdd中的I和j
int add = getAdd(ii[0], ii[1]);
Log.e("ethan", "两数相加结果"+add);
break;
case 1:
int jj[] = { 0, 0 };//此处数组长度必须和客户端发来的数组长度一致,不然报错
data.readIntArray(jj);//获取从客户端发来的int数组,对应的getminus中的I和j
int minus = getminus(jj[0],jj[1]);
Log.e("ethan", "两数相减结果"+minus);
break;
}
return false;
}
@Override
public int getAdd(int i, int j) {
return i+j;
}
@Override
public int getminus(int i, int j) {
return i-j;
}
}
MyBinder继承了Binder类同时实现了IEthan接口中的方法。同时在onTransact中得到客户端发过来的数据,并且解析,然后调用实现getAdd()和getminus();为什么要在onTransact执行方法,因为客户端拿到的是一个IBinder接口对象,会调用IBinder中的Transact()方法,由于Transact()方法被IBinder的实现类重写了,根据多态,客户端接口对象会调用Binder(实现类)中的Transact()方法,而Binder类中的Transact()内部又会调用onTransact(),因此最终会执行到onTransact()方法中。
下面就是服务端服务类了
public class MyService extends Service {
MyBinder mybinder = new MyBinder();
@Override
public IBinder onBind(Intent intent) {
Log.e("ethan", "Service binder==" + mybinder);
return mybinder;
}
@Override
public void onCreate() {
super.onCreate();
Log.e("ethan", "service on create");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("ethan", "service onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
}
服务端代码很简单,就是实例化一个MyBinder对象,然后在onBind方法中将这个对象返回出去就行了。
客户端代码
public class MainActivity extends Activity {
private IBinder myBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=(Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent("com.example.test.MyService");
bindService(intent, myConnection, Service.BIND_AUTO_CREATE);
}
});
}
ServiceConnection myConnection=new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("ethan", "service=="+service);
myBinder=service;
Parcel data1 = Parcel.obtain();
int ii[]={3,5};
data1.writeIntArray(ii);
Parcel data2 = Parcel.obtain();
Parcel data3 = Parcel.obtain();
int jj[]={5,3};
data3.writeIntArray(jj);
Parcel data4 = Parcel.obtain();
try {
myBinder.transact(0, data1,data2 , 1);
myBinder.transact(1, data3,data4 , 1);
} catch (RemoteException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
};
}
客户端代码很简单,定义了一个Button,点击的时候会执行bindService(intent, myConnection, Service.BIND_AUTO_CREATE); 其中intent是隐式启动Service的意图(隐式要给服务在AndroidManifest.xml中注册Action哈),myConnection 是ServiceConnection 接口的一个对象,该对象在MainActivity 初始化时就实例化。
此时所有的工作就做好了,可以愉快的事件进程间通信了。
03-24 04:41:11.363: E/ethan(16525): service==android.os.BinderProxy@526fc6f0
03-24 04:41:11.363: E/ethan(16168): 两数相加结果8
03-24 04:41:11.363: E/ethan(16168): 两数相减结果2
notes:
1,上述onServiceConnected(ComponentName name, IBinder service)方法中service参数其实就是服务端MyService中onBind方法中返回的MyBinder对象的代理对象。上述service==android.os.BinderProxy@526fc6f0可以看出。
2,虽然是service是代理对象,但是类型是Ibinder接口类型,也就是service是一个接口对象。
3,service 调用IBinder中的transact(),而IBinder被Binder实现并被服务端的MyBinder继承,因此执行MyBinder中的transact()方法,而transact()方法已被父类Binder重写调用onTransact(),最终会执行到MyBinder中的onTransact()。
4,myBinder.transact(0, data1,data2 , 1);中第一个参数对应服务端onTransact()的第一个参数,表示方法的Code。第二,第三是Parcel类型对象,该对象分别封装了一个int类型数组,也就是被用来相加和相减的俩个数字。
ps:aidl其实是系统自动对自己定义的接口(例如上述的IEthan)进行了一个封装。aidl中服务端需要实例化一个.stub对象,其实就是一个Binder对象,然后在service的onBinder方法中返回这个对象。客户端在onServiceConnected方法中对service进行强制类型转换成对应的接口对象,调用接口中的方法。其实就是在客户端进行了一次借口封装,然后接收onServiceConnected中的service。其后调用的接口种方法,本质还是通过transact来传送的,只不过是对其中的参数(例如Parcel对象)进行了打包而已。
有时间再写写Binder原理。