网站首页/安卓教程列表/内容

Android之Binder底层原理详细说明必读

安卓教程2022-06-11阅读
随着科技的发展,手机在我们的生活中扮演了很重要的角色,手机在便利我们生活的同时,也会对我们造成很重要的影响.手机已经成为了我们的必需品,不仅是通讯工具,而且很大程度上都是娱乐功能。手机应用主要指安装在智能手机上的软件,完善原始系统的不足与个性化。使手机完善其功能,为用户提供更丰富的使用体验的主要手段。

 什么是Binder

  Binder是Android中特有的一种跨进程通讯的方式。 但我们在平时的开发过程中, 可能很少用的。 而Binder的整个体系结构又尤为复杂, 一般很难通过网上的一两篇博客, 就能把Binder吃透, 我们需要通过源码及Binder的一些架构原理, 来进行研究。 后面的章节我们将主要通过3个部分来由浅至深来了解Binder。 首先我们先看在实际的开发中怎么来实现Binder通讯, 接着分析Binder框架的原理, 最后结合源码进行分析。

  为什么感觉Binder很陌生?

  1、项目业务简单, 不涉及多进程通讯

  2、涉及多进程通讯, 只简单用AIDL, 没深入了解

  为什么要学习Binder?

  Binder作为Android核心的跨进程通讯方式。 如果我们要研究Android的源码, Binder是一道需要跨过去的坎。 我们都知道系统的各种服务运行在SystemServer进程中, 我们应用与系统的各种交互都涉及到跨进程通讯。

  例如最简单的启动一个Activity、启动一个Service。 到例如使用系统的网络、硬件、等各种Service, 其实都涉及到跨进程通讯。 只是系统为我们做好了各种封装调用而已。

  所以如果你只希望一直停留在应用层的业务开发, 其实你可能一直永远都不知道Binder, 但是一旦你开始了解Android的源码, 那么你总会与Binder相遇。

  Android为什么使用Binder作为主要进程间通讯机制?

  1、安全性:Binder机制从协议本身就支持对通信双方做身份校检, 安全性高。 传统的进程通信方式对于通信双方的身份并没有做出严格的验证, 只有在上层协议上进行架设;比如Socket通信ip地址是客户端手动填入的, 都可以进行伪造

  2、性能:socket作为一款通用接口, 其传输效率低, 开销大, 主要用在跨网络的进程间通信和本机上进程间的低速通信, Binder其实通过Binder驱动在内核区域进行了数据的传输, 性能高

  如何实现一个Binder通讯?

  1、在项目中新建一个aidl

  // IBookManager.aidlpackagecom.jd.test.myapplication;importcom.jd.test.myapplication.Book; // Declare any non-default types here with import statementsinterfaceIBookManager{ /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */voidbasicTypes(intanInt, longaLong, booleanaBoolean,floataFloat, doubleaDouble, String aString); List<Book> getBooks(); voidaddBook(in Book book);}

  2、创建一个在独立进程的Service

  <service android: name= ".BookManagerService"android:process= ":remote"/>publicclassBookManagerServiceextendsService{ privateCopyOnWriteArrayList<Book> mBookList=newCopyOnWriteArrayList<>(); @OverridepublicvoidonCreate(){ super.onCreate(); mBookList.add(newBook( 1, "Android")); mBookList.add( newBook( 2, "IOS")); } privateBinder mBinder=newIBookManager.Stub(){ @OverridepublicvoidbasicTypes(intanInt, longaLong,booleanaBoolean, floataFloat, doubleaDouble, String aString)throwsRemoteException { }@OverridepublicList<Book> getBooks()throwsRemoteException { returnmBookList; }@OverridepublicvoidaddBook(Book book)throwsRemoteException { mBookList.add(book); } };@Nullable@OverridepublicIBinder onBind(Intent intent){ returnmBinder; }}

  3、另外一个进程, 启用远程的Service, 并调用接口方法, 进行通讯

  public classMainActivityextendsActivity{ @Overrideprotectedvoid onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intentintent= newIntent( this, BookManagerService. class); bindService(intent,mConnection, Context. BIND_AUTO_CREATE); }privateServiceConnectionmConnection= newServiceConnection() { @Overridepublic void onServiceConnected( ComponentNamecomponentName, IBinderiBinder) {IBookManagermanager= IBookManager. Stub.asInterface(iBinder); try{ List< Book> books=manager.getBooks(); System.out.println( "books:"+books); } catch( RemoteExceptione) { e.printStackTrace(); } } @Overridepublic void onServiceDisconnected(ComponentNamecomponentName) { } };}

  只能说so easy。 Android提供了优秀的API, 使得我们可以很方便的通过AIDL实现进程间的通讯。 貌似我们实现了进程间通讯, 但是连Binder的身影都没看到, 这也就是上面的Binder对很多童鞋都很陌生的原因。

  Binder的原理

  我们定义了AIDI后, 默认系统都会生成一个集成了IInterface的接口的类, eclipse默认是在gen目录下。

  Android之Binder底层原理详解必读

  /* * This file is auto-generated. DO NOT MODIFY. * Original file: G:SourceDemoMyApplicationappsrcmainaidlcomjdtestmyapplicationIBookManager.aidl */packagecom.jd.test.myapplication; // Declare any non-default types here with import statementspublicinterfaceIBookManagerextendsandroid.os.IInterface{ /** Local-side IPC implementation stub class. */publicstaticabstractclassStubextendsandroid.os.Binderimplementscom.jd.test.myapplication.IBookManager{ privatestaticfinaljava.lang.String DEOR = "com.jd.test.myapplication.IBookManager";/** Construct the stub at attach it to the interface. */publicStub(){ this.attachInterface( this, DEOR);}/** * Cast an IBinder object into an com.jd.test.myapplication.IBookManager interface, * generating a proxy if needed. */publicstaticcom.jd.test.myapplication. IBookManagerasInterface(android.os.IBinder obj){ if((obj== null)) { returnnull;}android.os.IInterface iin = obj.queryLocalInterface(DEOR); if(((iin!= null)&&(iininstanceofcom.jd.test.myapplication.IBookManager))) {return((com.jd.test.myapplication.IBookManager)iin);}returnnewcom.jd.test.myapplication.IBookManager.Stub.Proxy(obj);} @Overridepublicandroid.os.IBinder asBinder(){ returnthis;} @OverridepublicbooleanonTransact(intcode, android.os.Parcel data, android.os.Parcel reply, intflags)throwsandroid.os.RemoteException{ switch(code){caseINTERFACE_TRANSACTION:{reply.writeString(DEOR); returntrue;}caseTRANSACTION_basicTypes:{data.enforceInterface(DEOR); 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();returntrue;} caseTRANSACTION_getBooks:{data.enforceInterface(DEOR);java.util.List<com.jd.test.myapplication.Book> _result =this.getBooks();reply.writeNoException();reply.writeTypedList(_result); returntrue;}caseTRANSACTION_addBook:{data.enforceInterface(DEOR);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(); returntrue;}}returnsuper.onTransact(code, data, reply, flags);}privatestaticclassProxyimplementscom.jd.test.myapplication.IBookManager{privateandroid.os.IBinder mRemote;Proxy(android.os.IBinder remote){mRemote = remote;}@Overridepublicandroid.os. IBinder asBinder(){ returnmRemote;} publicjava.lang. StringgetInterfaceDeor(){ returnDEOR;} /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */@OverridepublicvoidbasicTypes(intanInt, longaLong,booleanaBoolean, floataFloat, doubleaDouble, java.lang.String aString)throwsandroid.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try{_data.writeInterfaceToken(DEOR);_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();}}@Overridepublicjava.util.List<com.jd.test.myapplication.Book> getBooks()throwsandroid.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(DEOR);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;}@OverridepublicvoidaddBook(com.jd.test.myapplication.Book book)throwsandroid.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try{_data.writeInterfaceToken(DEOR); 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();}}}staticfinalintTRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);staticfinalintTRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);staticfinalintTRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);} /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */publicvoidbasicTypes(intanInt, longaLong, booleanaBoolean, floataFloat, doubleaDouble, java.lang.String aString)throwsandroid.os.RemoteException;publicjava.util.List<com.jd.test.myapplication.Book> getBooks()throwsandroid.os.RemoteException; publicvoidaddBook(com.jd.test.myapplication.Book book)throwsandroid.os.RemoteException;}

  这个类也就是我们在onServiceConnected中使用的接口。 在这个IBookManager中我们有几个关键的类Stub、Proxy, 也见到了久违的Binder。 那么纠结Binder是怎么样来进行间通讯的呢?下面我们先通过一个示例图来简单描述一下该流程。

  Android之Binder底层原理详解必读

  IInterface结构分析

  首先变量DEOR定义了接口和对应方法的唯一标示。 因为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的引用

  publicBinder(){ init(); if(FIND_POTENTIAL_LEAKS) { finalClass<? extendsBinder> 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()); } } } privatenativefinalvoidinit();transact方法(往Binder驱动写数据)最后调用为BinderProxy代理类的transactpublicbooleantransact(intcode, Parcel data, Parcel reply,intflags)throwsRemoteException { Binder.checkParcel( this, code, data, "Unreasonably large binder buffer"); returntransactNative(code, data, reply, flags); }publicnativebooleantransactNative(intcode, Parcel data, Parcel reply,intflags)throwsRemoteException; onTransact方法 (Binder驱动回调数据)

  根据定义的常量标识, 解析Parcel数据, 回调本地方法

  @ Overridepublic boolean onTransact(int code, android.os. Parceldata, android.os.Parcelreply, int flags) throws android.os.RemoteException{ switch(code) { caseINTERFACE_TRANSACTION: {reply.writeString(DEOR); returntrue; }caseTRANSACTION_basicTypes: {data.enforceInterface(DEOR);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; } caseTRANSACTION_getBooks: { data.enforceInterface(DEOR);java.util.List<com.jd.test.myapplication. Book> _result = this.getBooks(); reply.writeNoException(); reply.writeTypedList(_result); return true; } caseTRANSACTION_addBook: {data.enforceInterface(DEOR);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方法
总结

  1、本文对Bidner做了一些整体的介绍, 主要是基于应用层的流程进行分析, 如果要彻底搞清楚Bidner, 可能还需阅读Binder驱动的源码及Bidner的协议等

  2、Binder在Android体系中, 有着非常重要的地位, 是核心的IPC方式。 如果希望学习Android源码, Binder是一道需要越过去的坎。


手机APP这些安装在手机里面丰富多彩的软件,让我们的生活更加方便和精彩。游戏、社交、购物、视频、音乐、学习......我们可以随时随地,随处进行。

……

相关阅读