众所周知Binder 是Android中的一种跨进程通信方式,从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager,WindowManager等等)和相应ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当是在不同的进程间通信时,服务端会返回一个bindService的代理对象,在同进程通信时,服务端会返回本地的一个bindService对象,同时Binder对象默认也继承了安卓智能指针类,框进程通信如图:
那么Binder驱动中,Binder对象引用有以下两种:
- 在同一个进程中,Binder对象的一个真实的虚拟地址
- 在另外一个进程中的,Binder对象的一个抽象的32位handle
因此在每个事务中,Binder驱动都会自动的将远程的binder handle映射成本地的地址,将本地的地址映射成远程的Binder handle,为更好的维护和管理Binder对象,其借助了一下几点:
- binder驱动在进程之间维持本地地址和远程handle(每一个进程都作为一个二进制树),因此,binder可以传送.
- 一旦一个新的Binder对象引用在事务中被发现,它就会记录在Binder驱动中.
- 任何时候,Binder引用与其它的进程共享,Binder驱动的引用计数器就会增长.
- 当进程消亡的时候,Binder驱动的计数器就会明确的减少或自动地减少.
-
当一个引用不再需要的时候,它的所有者就会提醒它可以释放掉,并且Binder会删除自己的映射.
接下来写一个基于Binder的服务端和客户端,其通信过程如图:
其服务端的binder实体:/** * @Author: beyondboy * @Gmail: guoli.xgl@alibaba-inc.com * @Date: 2016-05-20 * @Time: 19:21 * 服务端的binder对象 */ public class IFibonacciServiceImpl extends IFibonacciService.Stub{ private static final String TAG = "IFibonacciServiceImpl"; @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public long fibJR(long n) throws RemoteException { Log.d(TAG, String.format("fibJR(%d)", n)); return FibLib.fibJR(n); } @Override public long fibJI(long n) throws RemoteException { Log.d(TAG, String.format("fibJI(%d)", n)); return FibLib.fibJI(n); } @Override public long fibNR(long n) throws RemoteException { Log.d(TAG, String.format("fibNR(%d)", n)); return FibLib.fibNR(n); } @Override public long fibNI(long n) throws RemoteException { Log.d(TAG, String.format("fibNI(%d)", n)); return FibLib.fibNI(n); } /**返回响应给客户端*/ @Override public FibonacciResponse fib(FibonacciRequest request) throws RemoteException { Log.d(TAG, String.format("fib(%d, %s)", request.getN(), request.getType())); long timeInMillis = SystemClock.uptimeMillis(); long result; switch (request.getType()) { case ITERATIVE_JAVA: result = this.fibJI(request.getN()); break; case RECURSIVE_JAVA: result = this.fibJR(request.getN()); break; case ITERATIVE_NATIVE: result = this.fibNI(request.getN()); break; case RECURSIVE_NATIVE: result = this.fibNR(request.getN()); break; default: return null; } timeInMillis = SystemClock.uptimeMillis() - timeInMillis; return new FibonacciResponse(result, timeInMillis) { }; } }
服务端实体:
/** * @Author: beyondboy * @Gmail: guoli.xgl@alibaba-inc.com * @Date: 2016-05-20 * @Time: 20:38 * binder服务端 */ public class FibonacciService extends Service { private static final String TAG = "FibonacciService"; private IFibonacciServiceImpl service; @Override public void onCreate() { super.onCreate(); this.service = new IFibonacciServiceImpl(); Log.d(TAG, "onCreate()'ed"); } @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind()'ed"); return this.service; } @Override public boolean onUnbind(Intent intent) { Log.d(TAG, "onUnbind()'ed"); return super.onUnbind(intent); } @Override public void onDestroy() { Log.d(TAG, "onDestroy()'ed"); this.service = null; super.onDestroy(); } }
客户端实体:
/** * @Author: beyondboy * @Gmail: guoli.lxgl@alibaba-inc.com * @Date: 2016-05-21 * @Time: 12:42 * 客户端 */ public class FibonacciActivity extends Activity implements View.OnClickListener,ServiceConnection{ private static final String TAG = "FibonacciActivity"; private EditText input; private Button button; // 触发非波那契數列的计算 private RadioGroup type; // 非波那契數列实现类型 private TextView output; // 输出非波那契數列的计算结果 private IFibonacciService service; // 引用非波那契數列的服务 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.setContentView(R.layout.main); this.input = (EditText) super.findViewById(R.id.input); this.button = (Button) super.findViewById(R.id.button); this.type = (RadioGroup) super.findViewById(R.id.type); this.output = (TextView) super.findViewById(R.id.output); this.button.setOnClickListener(this); // 当我们链接上我们的服务才让按钮变为可按 this.button.setEnabled(false); } @Override protected void onResume() { // Log.d(TAG, "onResume()'ed"); super.onResume(); Intent intent=new Intent(this, FibonacciService.class); // intent.addCategory(Intent.CATEGORY_DEFAULT); //开启服务 if (!super.bindService(intent, this, BIND_AUTO_CREATE)) { Log.w(TAG, "Failed to bind to service"); } } @Override protected void onPause() { Log.d(TAG, "onPause()'ed"); super.onPause(); //停止服务 super.unbindService(this); } public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG, "onServiceConnected()'ed to " + name); // 获得我们的IFibonacciService this.service = IFibonacciService.Stub.asInterface(service); this.button.setEnabled(true); } public void onServiceDisconnected(ComponentName name) { Log.d(TAG, "onServiceDisconnected()'ed to " + name); this.service = null; this.button.setEnabled(false); } public void onClick(View view) { final long n; String s = this.input.getText().toString(); if (TextUtils.isEmpty(s)) { return; } try { n = Long.parseLong(s); } catch (NumberFormatException e) { this.input.setError(super.getText(R.string.input_error)); return; } final FibonacciRequest.Type type; switch (FibonacciActivity.this.type.getCheckedRadioButtonId()) { case R.id.type_fib_jr: type = FibonacciRequest.Type.RECURSIVE_JAVA; break; case R.id.type_fib_ji: type = FibonacciRequest.Type.ITERATIVE_JAVA; break; case R.id.type_fib_nr: type = FibonacciRequest.Type.RECURSIVE_NATIVE; break; case R.id.type_fib_ni: type = FibonacciRequest.Type.ITERATIVE_NATIVE; break; default: return; } final FibonacciRequest request = new FibonacciRequest(n, type); // 展示计算进度 final ProgressDialog dialog = ProgressDialog.show(this, "", super.getText(R.string.progress_text), true); new AsyncTask<Void, Void, String>() { @Override protected String doInBackground(Void... params) { try { long totalTime = SystemClock.uptimeMillis(); FibonacciResponse response = FibonacciActivity.this.service .fib(request); totalTime = SystemClock.uptimeMillis() - totalTime; // 获取结果 return String.format(Locale.ENGLISH, "fibonacci(%d)=%d\nin %d ms\n(+ %d ms)", n, response.getResult(), response.getTimeInMillis(), totalTime - response.getTimeInMillis()); } catch (RemoteException e) { Log.wtf(TAG, "Failed to communicate with the service", e); return null; } } @Override protected void onPostExecute(String result) { dialog.dismiss(); if (result == null) { // 处理错误 Toast.makeText(FibonacciActivity.this, R.string.fib_error, Toast.LENGTH_SHORT).show(); } else { // 展示结果 FibonacciActivity.this.output.setText(result); } } }.execute(); } }
demo链接如下:
https://github.com/scau-beyondboy/BinderSynDemo.git
其大致结构如图:
在binder通信中,有个重要细节:
客户端和服务端都位于同一个进程时,其通信不会通过Stub的内部代理类Proxy来完成,也就是说方法调用不会走跨进程的transact过程,而当两者位于不同进程时,会通过内部代理类的Proxy的transact过程。//Construct the stub at attach it to the interface. public Stub() { //存储此binder子类对象到本地进程的binder节点树中 this.attachInterface(this, DESCRIPTOR); } // Cast an IBinder object into an com.scau.beyondboy.bindercommon.IFibonacciService interface, //generating a proxy if needed. public static com.scau.beyondboy.bindercommon.IFibonacciService asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } //从本进程binder节点树中查找 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.scau.beyondboy.bindercommon.IFibonacciService))) { return ((com.scau.beyondboy.bindercommon.IFibonacciService) iin); } //创建其binder代理对象暴露给另一个进程使用其服务 return new com.scau.beyondboy.IFibonacciService.Stub.Proxy(obj); }