平安彩票网 - 专业热门高频彩投注开奖官网

2017/5/17 來源:www.arpun.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); 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是一道需要越過去的坎。

網友評論
評論(...
全部評論