依靠aidl编写Binder间的通信,确实很方便,只要写好接口,Android Studio编译一下,就会在Project目录->app->generated->source->aidl->debug->包名目录下自动生成代码。为了加深理解,本文决定自己手动实现Binder客户端和服务端的通信,参考了android开发艺术探索这本书籍。
先开始编写服务端,首先自定义一个接口,这个接口继承自IInterface,接着声明一个函数,提供给客户端调用,并且给这个函数定义一个ID。
public interface ICalculationManager extends IInterface {
private static final int TRANSACTION_doCalculate = (IBinder.FIRST_CALL_TRANSACTION + 0);
double doCalculate(double a, double b) throws RemoteException;
}
在客户端绑定服务端的时候,会调用Server的onBind,我们需要返回一个Binder对象,并要实现声明的函数。
public class MyServer extends Service {
private static final String TAG = "MyServer";
private final ICalculationManager.Stub mBinder = new ICalculationManager.Stub() {
@Override
public double doCalculate(double a, double b) {
return a + b;
}
};
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
return mBinder;
}
}
现在可以实现Stub,Stub继承Binder类并实现了接口ICalculationManager,增加了DESCRIPTOR,实现了Stub的构造函数。最重要的是onTransact这个函数,这个函数用来处理客户端要调用的函数和传来的参数,并将结果返回。来看它的第一个参数code,code指的就是函数的ID,通过客户端发过来的id,来决定调用哪个函数。第二个参数data,就是客户端传过来的参数。第三个参数reply用来存放返回结果。这里onTransact里用根据code里的id号switch语句中对应分支,首先从data中读出参数,再经过this.doCalculate的计算,得到结果,写到reply中,this.doCalculate实现就是我们在MyServer中重写的doCalculate。还有其中的asBinder返回的是当前Binder对象,这里就是返回Stub对象。
public interface ICalculationManager extends IInterface {
private static final String DESCRIPTOR ="com.weather.manual.ICalculationManager";
private static final int TRANSACTION_doCalculate = (IBinder.FIRST_CALL_TRANSACTION + 0);
double doCalculate(double a, double b) throws RemoteException;
abstract class Stub extends Binder implements ICalculationManager{
public Stub(){this.attachInterface(this,DESCRIPTOR);}
@Override
public IBinder asBinder() {
return this;
}
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code){
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_doCalculate:
{
data.enforceInterface(DESCRIPTOR);
double _arg0;
_arg0 = data.readDouble();
double _arg1;
_arg1 = data.readDouble();
double _result = this.doCalculate(_arg0,_arg1);
reply.writeNoException();
reply.writeDouble(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
}
服务端的Binder写完了,还要在xml中添加server信息。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.weather.manualserver">
<application
/*
......
*/
<service android:name=".MyServer">
<intent-filter>
<action android:name="com.weather.manualserver.MyServer"></action>
</intent-filter>
</service>
</application>
</manifest>
在MainActivity中添加启动server的代码。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this,MyServer.class);
startService(intent);
}
}
接下来开始编写客户端里的代码,定义一个ServiceConnection对象,与Service连上就返回一个对象,我们这里返回的是Binder代理对象mICalculationManager,因为是不同进程间的通信。在onCreate中的onClick中调用doCalculate。我们要实现asInterface的代码和客户端doCalculate的代码。
ICalculationManager mICalculationManager;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
mICalculationManager = ICalculationManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected: ");
mICalculationManager = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText_one = (EditText)findViewById(R.id.ed_one);
editText_two = (EditText)findViewById(R.id.ed_two);
textView_result = (TextView)findViewById(R.id.tv_result);
Intent intent = new Intent("com.weather.manualserver.MyServer");
bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);
findViewById(R.id.btn_clicked).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
double one = Double.parseDouble(editText_one.getText().toString());
double two = Double.parseDouble(editText_two.getText().toString());
double result = mICalculationManager.doCalculate(one,two);
textView_result.setText(String.valueOf(result));
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
接下来实现asInterface的代码,传进来的参数obj是驱动提供给我们的,这里根据判断的是本地进程通信还是进程间通信,
是进程间通信就返回代理对象,是本地通信,就直接返回Binder对象本体。
public static ICalculationManager asInterface(IBinder obj){
if(obj == null){
return null;
}
Log.d(TAG, "asInterface: ");
IInterface iInterface = obj.queryLocalInterface(DESCRIPTOR);
if((iInterface!=null)&&(iInterface instanceof ICalculationManager)){
return (ICalculationManager)iInterface;
}
return new ICalculationManager.Stub.Proxy(obj);
}
然后看代理对象的实现,代理对象中,主要看实现了doCalculate。将参数写入data中,并调用transact函数,传入的参数是要调用的函数的id号,还有参数、返回值和标志位,与onTransact函数的参数很像,执行完后,从reply中读取结果返回。
private static class Proxy implements ICalculationManager {
private IBinder mRemote;
private Proxy(IBinder remote){
mRemote = remote;
}
@Override
public double doCalculate(double a, double b) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
double _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeDouble(a);
_data.writeDouble(b);
mRemote.transact(Stub.TRANSACTION_doCalculate,_data,_reply,0);
_reply.readException();
_result = _reply.readDouble();
}finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public IBinder asBinder() {
return mRemote;
}
}