什么是Binder
Binder是Android中特有的一种跨进程通讯的方式。但我们在平时的开发过程中,可能很少用的。而Binder的整个体系结构又尤为复杂,一般很难通过网上的一两篇博客,就能把Binder吃透,我们需要通过源码及Binder的一些架构原理,来进行研究。后面的章节我们将主要通过3个部分来由浅至深来了解Binder。首先我们先看在实际的开发中怎么来实现Binder通讯,接着分析Binder框架的原理,最后结合源码进行分析。
为什么感觉Binder很陌生?
项目业务简单,不涉及多进程通讯
涉及多进程通讯,只简单用AIDL,没深入了解
为什么要学习Binder?
Binder作为Android核心的跨进程通讯方式。如果我们要研究Android的源码,Binder是一道需要跨过去的坎。我们都知道系统的各种服务运行在SystemServer进程中,我们应用与系统的各种交互都涉及到跨进程通讯。
例如最简单的启动一个Activity、启动一个Service。到例如使用系统的网络、硬件、等各种Service,其实都涉及到跨进程通讯。只是系统为我们做好了各种封装调用而已。
所以如果你只希望一直停留在应用层的业务开发,其实你可能一直永远都不知道Binder,但是一旦你开始了解Android的源码,那么你总会与Binder相遇。
Android为什么使用Binder作为主要进程间通讯机制?
安全性:Binder机制从协议本身就支持对通信双方做身份校检,安全性高。传统的进程通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设;比如Socket通信ip地址是客户端手动填入的,都可以进行伪造
性能:socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信,Binder其实通过Binder驱动在内核区域进行了数据的传输,性能高
如何实现一个Binder通讯?
在项目中新建一个aidl
// IBookManager.aidlpackage com.jd.test.myapplication;import com.jd.test.myapplication.Book;// Declare any non-default types here with import statementsinterface IBookManager { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); List<Book> getBooks(); void addBook(in Book book); }
创建一个在独立进程的Service
<service android:name=".BookManagerService" android:process=":remote"/>
public class BookManagerService extends Service { private CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList<>(); @Override public void onCreate() { super.onCreate(); mBookList.add(new Book(1,"Android")); mBookList.add(new Book(2,"IOS")); } private Binder mBinder=new IBookManager.Stub(){ @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public List<Book> getBooks() throws RemoteException { return mBookList; } @Override public void addBook(Book book) throws RemoteException { mBookList.add(book); } }; @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } }
另外一个进程,启用远程的Service,并调用接口方法,进行通讯
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent=new Intent(this,BookManagerService.class); bindService(intent,mConnection, Context.BIND_AUTO_CREATE); } private ServiceConnection mConnection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { IBookManager manager=IBookManager.Stub.asInterface(iBinder); try { List<Book> books=manager.getBooks(); System.out.println("books:"+books); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { } }; }
只能说so easy。Android提供了优秀的API,使得我们可以很方便的通过AIDL实现进程间的通讯。貌似我们实现了进程间通讯,但是连Binder的身影都没看到,这也就是上面的Binder对很多童鞋都很陌生的原因。
Binder的原理
我们定义了AIDI后,默认系统都会生成一个集成了IInterface的接口的类,eclipse默认是在gen目录下。
/* * This file is auto-generated. DO NOT MODIFY. * Original file: G:\\Source\\Demo\\MyApplication\\app\\src\\main\\aidl\\com\\jd\\test\\myapplication\\IBookManager.aidl */package com.jd.test.myapplication;// Declare any non-default types here with import statementspublic interface IBookManager extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.jd.test.myapplication.IBookManager{private static final java.lang.String DESCRIPTOR = "com.jd.test.myapplication.IBookManager";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR); }/** * Cast an IBinder object into an com.jd.test.myapplication.IBookManager interface, * generating a proxy if needed. */public static com.jd.test.myapplication.IBookManager asInterface(android.os.IBinder obj){if ((obj==null)) {return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.jd.test.myapplication.IBookManager))) {return ((com.jd.test.myapplication.IBookManager)iin); }return new com.jd.test.myapplication.IBookManager.Stub.Proxy(obj); }@Override public android.os.IBinder asBinder(){return this; }@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{switch (code) {case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR);return true; }case TRANSACTION_basicTypes: { data.enforceInterface(DESCRIPTOR);int _arg0; _arg0 = data.readInt();long _arg1; _arg1 = data.readLong();boolean _arg2; _arg2 = (0!=data.readInt());float _arg3; _arg3 = data.readFloat();double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString();this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException();return true; }case TRANSACTION_getBooks: { data.enforceInterface(DESCRIPTOR); java.util.List<com.jd.test.myapplication.Book> _result = this.getBooks(); reply.writeNoException(); reply.writeTypedList(_result);return true; }case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.jd.test.myapplication.Book _arg0;if ((0!=data.readInt())) { _arg0 = com.jd.test.myapplication.Book.CREATOR.createFromParcel(data); }else { _arg0 = null; }this.addBook(_arg0); reply.writeNoException();return true; } }return super.onTransact(code, data, reply, flags); }private static class Proxy implements com.jd.test.myapplication.IBookManager{private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; }@Override public android.os.IBinder asBinder(){return mRemote; }public java.lang.String getInterfaceDescriptor(){return DESCRIPTOR; }/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException{ android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain();try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean)?(1):(0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString); mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException(); }finally { _reply.recycle(); _data.recycle(); } }@Override public java.util.List<com.jd.test.myapplication.Book> getBooks() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.jd.test.myapplication.Book> _result;try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.jd.test.myapplication.Book.CREATOR); }finally { _reply.recycle(); _data.recycle(); }return _result; }@Override public void addBook(com.jd.test.myapplication.Book book) throws android.os.RemoteException{ android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain();try { _data.writeInterfaceToken(DESCRIPTOR);if ((book!=null)) { _data.writeInt(1); book.writeToParcel(_data, 0); }else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0); _reply.readException(); }finally { _reply.recycle(); _data.recycle(); } } }static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); }/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;public java.util.List<com.jd.test.myapplication.Book> getBooks() throws android.os.RemoteException;public void addBook(com.jd.test.myapplication.Book book) throws android.os.RemoteException; }
这个类也就是我们在onServiceConnected中使用的接口。在这个IBookManager中我们有几个关键的类Stub、Proxy,也见到了久违的Binder。那么纠结Binder是怎么样来进行间通讯的呢?下面我们先通过一个示例图来简单描述一下该流程。
IInterface结构分析
首先变量DESCRIPTOR定义了接口和对应方法的唯一标示。因为Binder其实是一种底层的通讯方式,Google工程师将Binder包装成了一种对象的引用。所以这里的标识是告诉底层的Binder驱动,我的Binder引用标识,对应的方法标识。这样Binder驱动才能在进程间进行装换。
Proxy:实现了IBookManager接口,这个代理类是往Binder驱动里面写数据,通过调用Binder的transact方法往Binder驱动写Parcel数据。可以理解为告诉Binder驱动我们要调用什么方法
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
Stub:继承了Binder,实现了IBookManager接口。Binder驱动调用远程方法成功后,要回调告诉执行的结果。通过回调onTransact方法。将Binder驱动中的Parcel数据转换为我们的回调数据。
IBinder引用是什么时候注册到了Binder驱动中呢?
我们知道Stub继承了Binder,那么当Stub实例化的时候,这个时候Binder无参构造被调用,执行了一个native 的init方法。这个时候向Binder驱动注册了IBinder的引用
public Binder() { init(); if (FIND_POTENTIAL_LEAKS) { final Class<? extends Binder> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Binder class should be static or leaks might occur: " + klass.getCanonicalName()); } } }private native final void init();
transact方法(往Binder驱动写数据)最后调用为BinderProxy代理类的transact
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { Binder.checkParcel(this, code, data, "Unreasonably large binder buffer"); return transactNative(code, data, reply, flags); } public native boolean transactNative(int code, Parcel data, Parcel reply, int flags) throws RemoteException;
onTransact方法 (Binder驱动回调数据)
根据定义的常量标识,解析Parcel数据,回调本地方法
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_basicTypes: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0 != data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } case TRANSACTION_getBooks: { data.enforceInterface(DESCRIPTOR); java.util.List<com.jd.test.myapplication.Book> _result = this.getBooks(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.jd.test.myapplication.Book _arg0; if ((0 != data.readInt())) { _arg0 = com.jd.test.myapplication.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBook(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); }
Binder应用层源码实现流程分析
我们了解了Binder通讯的一些基础原理后,通过应用层的调用来追踪整个执行的流程。下面我们通过一个表格索引来描述Binder通讯的结构
索引 | 调用的类间关系 | 说明 |
---|---|---|
1 | Activity:bindService | 执行服务的绑定 |
2 | Context:bindService | 基类的的服务绑定抽象方法 |
3 | ContextImpl:bindService | Context实现类的方法 |
4 | ContextImpl:bindServiceCommon | 进行一些Intent校验等,调用ActivityManagerNative.getDefault().bindService |
5 | ActivityManagerService:bindService | 进行一些合法 非空的校验 |
6 | ActiveServices:bindServiceLocked | 创建Service,对服务进行缓存记录,同时回调了connection方法 |
7 | ActiveServices:requestServiceBindingLocked | 校验服务进程是否存在,调用ApplicationThread的scheduleBindService |
8 | ApplicationThread:scheduleBindService | ApplicationThread是ActivityThread的内部类,该方法发送了一个Message sendMessage(H.BIND_SERVICE, s); |
9 | ActiviThread:handleBindService | 校验Service是否存在,执行AMS的publishService方法 |
10 | ActivityManagerService:publishService | 校验及调用ActiveServices的publishServiceLocked方法 |
11 | ActiveServices:publishServiceLocked | 执行Binder的restoreCallingIdentity方法 |
总结
本文对Bidner做了一些整体的介绍,主要是基于应用层的流程进行分析,如果要彻底搞清楚Bidner,可能还需阅读Binder驱动的源码及Bidner的协议等
Binder在Android体系中,有着非常重要的地位,是核心的IPC方式。如果希望学习Android源码,Binder是一道需要越过去的坎。
共同学习,写下你的评论
评论加载中...
作者其他优质文章