本文作者:qiaoqingyi

android网络编程pdf(android 网络编程)

qiaoqingyi 02-24 113

  作者

  本文由 黄俊彬投稿,博客地址:https://huangjunbin.com/

  什么是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); ListBook getBooks(); voidaddBook(in Book book);}

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

  service android: name= ".BookManagerService"android:process= ":remote"/ publicclassBookManagerServiceextendsService{ privateCopyOnWriteArrayListBook 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 { } @OverridepublicListBook getBooks()throwsRemoteException { returnmBookList; } @OverridepublicvoidaddBook(Book book)throwsRemoteException { mBookList.add(book); } }; @Nullable@OverridepublicIBinder onBind(Intent intent){ returnmBinder; }}

android网络编程pdf(android 网络编程)

  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目录下。

  

  /* * 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. IBookManager asInterface(android.os.IBinder obj){ if((obj== null)) { returnnull;}android.os.IInterface iin = obj.queryLocalInterface(DEOR); if(((iin!= null)(iin instanceofcom.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.Listcom.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. String getInterfaceDeor(){ 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.Listcom.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.Listcom.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.Listcom.jd.test.myapplication.Book getBooks() throwsandroid.os.RemoteException; publicvoidaddBook(com.jd.test.myapplication.Book book)throwsandroid.os.RemoteException;}

  这个类也就是我们在onServiceConnected中使用的接口。在这个IBookManager中我们有几个关键的类Stub、Proxy,也见到了久违的Binder。那么纠结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. Listcom.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是一道需要越过去的坎。

  3、查看详细源码请点击“阅读原文”,可以和作者交流,欢迎大家点赞和评论。

  大家都在看

  2017年谷歌I/O大会精彩看点细数【Bus Weekly】49期

  显示地图以及定位,一篇文章玩转高德地图

  浅淡MVP的实战演习,让代码结构更简单~

  送上自定义View实现小例一个,请接收!

阅读
分享