通过一个实例来展示如何在Java层使用Android binder进行跨进程通讯。
概况
我们要实现一个计算器的例子,服务端用Service实现,它可以计算加减乘除等各种运算,
客户端是一个Activity,通过绑定到服务端,调用服务端的接口进行计算,然后把计算结果
展示出来(TextView)。
定义业务接口
对于我们这个例子来说,业务就是加减乘除各种计算,所以业务接口也就是计算器的各种接口。
ICalc.java1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.ybin.calc.calculator;
import android.os.IBinder; import android.os.IInterface; import android.os.RemoteException;
public interface ICalc extends IInterface { static final String DESCRIPTOR = "com.example.calcservice"; static final int TRANSACTION_add = (IBinder.FIRST_CALL_TRANSACTION + 0);
int add(int a, int b) throws RemoteException; }
|
native部分
也就是service部分,真正的计算在这里进行,
CalcNative.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package com.ybin.calc.calculator;
import android.os.Binder; import android.os.IBinder; import android.os.IInterface; import android.os.Parcel; import android.os.RemoteException;
public class CalcNative extends Binder implements ICalc { public static ICalc asInterface(IBinder obj) { IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if(iin != null) { return (ICalc)iin; } return new CalcProxy(obj); }
@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_add: { data.enforceInterface(DESCRIPTOR); int a = data.readInt(); int b = data.readInt(); int result = this.add(a, b); reply.writeNoException(); reply.writeInt(result); return true; } } return super.onTransact(code, data, reply, flags); }
@Override public int add(int a, int b) { return a + b; } }
|
proxy部分
也就是客户端的业务接口,它通过代理模式对binder的proxy部分进行打包,
CalcProxy.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package com.ybin.calc.calculator;
import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException;
public class CalcProxy implements ICalc { private IBinder mRemote; public CalcProxy(IBinder remote) { mRemote = remote; }
@Override public IBinder asBinder() { return mRemote; }
@Override public int add(int a, int b) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); int result = 0; try { data.writeInterfaceToken(DESCRIPTOR); data.writeInt(a); data.writeInt(b); mRemote.transact(TRANSACTION_add, data, reply, 0); reply.readException(); result = reply.readInt(); } finally { data.recycle(); reply.recycle(); } return result; } }
|
实现Service
我们通过Service来提供服务,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.ybin.calc.calcservice;
import com.ybin.calc.calculator.CalcNative;
import android.app.Service; import android.content.Intent; import android.os.IBinder;
public class CalcService extends Service {
@Override public IBinder onBind(Intent intent) { return new CalcNative(); }
}
|
实现client
客户端就是一个简单的activity而已,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| package com.ybin.calc.calcclient;
import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.widget.TextView;
import com.ybin.calc.R; import com.ybin.calc.calculator.CalcNative; import com.ybin.calc.calculator.ICalc;
public class CalcClient extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.calcclient_main);
ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { ICalc calc = CalcNative.asInterface(service); int r = 0; try { r = calc.add(1, 2); } catch (RemoteException e) { Log.e("CalcClient", "call service interface error"); e.printStackTrace(); } TextView tv = (TextView) findViewById(R.id.text); tv.setText("1 + 2 = " + r); }
@Override public void onServiceDisconnected(ComponentName name) { } }; Intent intent = new Intent("com.ybin.calc.service");
bindService(intent, conn, BIND_AUTO_CREATE); } }
|
OK,现在运行即可。不过你可能会有疑问,client和service在同一个进程中啊,这哪里是进程间通讯啊,
好,我们设置一下manifest文件,
1 2 3 4 5 6 7
| <service android:name=".calcservice.CalcService" android:process="com.ybin.calc.service"> <intent-filter> <action android:name="com.ybin.calc.service" /> </intent-filter> </service>
|
现在可以进行IPC了。
其实,android已经提供了AIDL来简化这个过程,你只需定义aidl文件,即业务逻辑,proxy, native部分的
binder内容自动生成。
本文源码:github
(over)