这次我们来分析Binder对象。用来更好的去理解AIDL
Binder分为三部分,服务端,客户端,和Binder驱动。
首先讲下Binder驱动:
Binder它的作用是进行进程间通信的。既然它能提供进程间通信的功能。那么它应该是系统级别的全局性。
而Binder驱动存在于Android内核中,它能提供全局的服务。
Binder服务端:
一个Binder的服务端,就是一个Binder对象,当我们new 出一个Binder对象
那么它内部会起一个线程。用于接收Binder驱动发送的消息。并且Binder驱动 会创建了一个 mRemote对象。它也是个Binder对象。mRemote是当不同进程间通信的时候会使用到。
我们可以说。每个服务端的Binder对象。在Binder驱动中都会有一个对应的mRemote对象。当不同进程间访问的时候,客户端需要获得mRemote对象的引用。
Binder客户端:
这里我们首先说一下Binder的两个方法
transact(),和onTransact(),这两个方法是对应的。
而我们的客户端就是获得服务端的Binder对象引用(同进程),或者是mRemote的引用(不同进程)。最后然后去调用transact()方法。
这里我们测试下同进程间客户端获取的Service与服务端所创建的
Service是否是一个对象。
我们修改一下与提供计算的Service同进程中的
Activity中的代码(实际这个Activity就是客户端了)
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
protected void onResume() {
super.onResume();
//startService(new Intent(this,MyService.class));
}
ServiceConnection conn = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
}
public void onServiceConnected(ComponentName name, IBinder service) {
IJiaFa mService= Stub.asInterface(service);
try {
Log.i("TAG", "同进程间的mService="+mService);
Log.i("TAG", "从服务端算出的结果是+"+mService.add(5, 10));
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
public void onClick(View v){
bindService(new Intent("com.example.jiafa"), conn, BIND_AUTO_CREATE);
}
}
Service中的代码块(服务端)
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
mBinder binder=new mBinder();
Log.i("TAG", "服务端的binder="+binder);
return binder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
class mBinder extends IJiaFa.Stub{
public int add(int x, int y) throws RemoteException {
return (x+y)*10;
}
}
}
输出结果。
可以看到,确实是同一个对象
客户端实际就是获得了Binder对象的引用后。然后调用transact()方法
transact()方法需要三个参数
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
对应的 onTransact()方法前三个参数。
protected boolean onTransact(int code, Parcel data, Parcel reply,int flags) throws RemoteException {
//代码略
}
当我们客户端调用transact()会有两种情况。如果服务端的Binder没有重写
transact()方法,那么走的逻辑就是transact()中调用onTransact()
还有一种情况就是 我们重写了transact(),而mRemote对象就是重写过的。重写的内容包括:
换句话说,当不同进程间通信。客户端获取mRemote对象的引用后。调用
transact()方法,会挂起当前客户端线程。并且把参数发送给了服务端。
服务端运行完后。会将客户端线程唤醒。所以当我们知道服务端的是耗时操作那么就不应该在UI线程去执行transact()方法。
以上我们就Binder对象的三个模块。
现在我们分析下transact()方法的三个参数
code:code一般是用来做方法标示的。当服务端有多个方法的时候,可以根据传入code的数字,服务端相应的提供方法。
data: 存放的是数据。客户端调用服务端的方法的时候,需要的数据就封装到里面。
reply:当服务端方法有返回值的时候,可以将数据写入进去。
onTransact()中的前三个参数是一一对应的。
不过需要注意的是:onTransact()的返回值为false的时候。客户端的请求会失败。
现在我们按自己的理论,来利用Binder进行进程间通信。不去利用AIDL
这里我们提出疑问。我们的客户端怎么去获得Binder对象的引用。
Android给我们提供的方式就是。当bindService()在
中。传递的service,就是系统给我们的Binder对象引用。
那么开始;
首先我们说下思路。
服务端的思路:
1.我们服务端的Binder对象,必须重写onTransact(),因为不重写就没办法去调用我们自己定义的方法了。
2.我们定义方法标示。
3.按一定的顺序从data中获取数据。
4.如果需要返回值,我们返回添加到reply中。
贴上代码:
public class MyService extends Service {
final static int M_ADD=101;
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return new mBinder();
}
class mBinder extends Binder{
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case M_ADD:
int agr0=data.readInt();
int agr1=data.readInt();
reply.writeInt(add(agr0,agr1));
return true;
default:
break;
}
return super.onTransact(code, data, reply, flags);
}
}
public int add(int x,int y){
return x*10+y*5;
}
}
客户端的思路:
1.获取Binder对象的引用,然后调用transact()方法。
2.我们需要准备transact()的三个参数。
贴上代码
public class MainActivity extends Activity {
ServiceConnection conn;
final static int M_ADD=110;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
conn=new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Parcel data= Parcel.obtain();
Parcel reply=Parcel.obtain();
try {
data.writeInt(10);
data.writeInt(5);
service.transact(M_ADD, data, reply, 0);
Log.i("TAG", "获得的算术结果是="+reply.readInt());
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
}
public void onClick(View v){
bindService(new Intent("com.example.jiafa"), conn, BIND_AUTO_CREATE);
}
}
好了,终于到出结果的时候了。运行—–
总结:我们可以看到不利用AIDL也可以完成进程间通信,
这里我们可以在回去看下系统帮助我们生产的代码。实际上一部分是客户端
使用的,一部分是服务端使用的。