隨著科技的發展,手機在我們的生活中扮演了很重要的角色,手機在便利我們生活的同時,也會對我們造成很重要的影響.手機已經成為了我們的必需品,不僅是通訊工具,而且很大程度上都是娛樂功能。手機應用主要指安裝在智能手機上的軟件,完善原始系統的不足與個性化。使手機完善其功能,為用戶提供更豐富的使用體驗的主要手段。 什么是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目錄下。 /* * 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是怎么樣來進行間通訊的呢?下面我們先通過一個示例圖來簡單描述一下該流程。 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、本文對Bidner做了一些整體的介紹, 主要是基于應用層的流程進行分析, 如果要徹底搞清楚Bidner, 可能還需閱讀Binder驅動的源碼及Bidner的協議等 2、Binder在Android體系中, 有著非常重要的地位, 是核心的IPC方式。 如果希望學習Android源碼, Binder是一道需要越過去的坎。 手機APP這些安裝在手機里面豐富多彩的軟件,讓我們的生活更加方便和精彩。游戲、社交、購物、視頻、音樂、學習......我們可以隨時隨地,隨處進行。 |
溫馨提示:喜歡本站的話,請收藏一下本站!