隨著科技的發(fā)展,手機(jī)在我們的生活中扮演了很重要的角色,手機(jī)在便利我們生活的同時(shí),也會(huì)對(duì)我們?cè)斐珊苤匾挠绊?手機(jī)已經(jīng)成為了我們的必需品,不僅是通訊工具,而且很大程度上都是娛樂(lè)功能。手機(jī)應(yīng)用主要指安裝在智能手機(jī)上的軟件,完善原始系統(tǒng)的不足與個(gè)性化。使手機(jī)完善其功能,為用戶(hù)提供更豐富的使用體驗(yàn)的主要手段。 什么是Binder Binder是Android中特有的一種跨進(jìn)程通訊的方式。 但我們?cè)谄綍r(shí)的開(kāi)發(fā)過(guò)程中, 可能很少用的。 而B(niǎo)inder的整個(gè)體系結(jié)構(gòu)又尤為復(fù)雜, 一般很難通過(guò)網(wǎng)上的一兩篇博客, 就能把Binder吃透, 我們需要通過(guò)源碼及Binder的一些架構(gòu)原理, 來(lái)進(jìn)行研究。 后面的章節(jié)我們將主要通過(guò)3個(gè)部分來(lái)由淺至深來(lái)了解Binder。 首先我們先看在實(shí)際的開(kāi)發(fā)中怎么來(lái)實(shí)現(xiàn)Binder通訊, 接著分析Binder框架的原理, 最后結(jié)合源碼進(jìn)行分析。 為什么感覺(jué)Binder很陌生? 1、項(xiàng)目業(yè)務(wù)簡(jiǎn)單, 不涉及多進(jìn)程通訊 2、涉及多進(jìn)程通訊, 只簡(jiǎn)單用AIDL, 沒(méi)深入了解 為什么要學(xué)習(xí)Binder? Binder作為Android核心的跨進(jìn)程通訊方式。 如果我們要研究Android的源碼, Binder是一道需要跨過(guò)去的坎。 我們都知道系統(tǒng)的各種服務(wù)運(yùn)行在SystemServer進(jìn)程中, 我們應(yīng)用與系統(tǒng)的各種交互都涉及到跨進(jìn)程通訊。 例如最簡(jiǎn)單的啟動(dòng)一個(gè)Activity、啟動(dòng)一個(gè)Service。 到例如使用系統(tǒng)的網(wǎng)絡(luò)、硬件、等各種Service, 其實(shí)都涉及到跨進(jìn)程通訊。 只是系統(tǒng)為我們做好了各種封裝調(diào)用而已。 所以如果你只希望一直停留在應(yīng)用層的業(yè)務(wù)開(kāi)發(fā), 其實(shí)你可能一直永遠(yuǎn)都不知道Binder, 但是一旦你開(kāi)始了解Android的源碼, 那么你總會(huì)與Binder相遇。 Android為什么使用Binder作為主要進(jìn)程間通訊機(jī)制? 1、安全性:Binder機(jī)制從協(xié)議本身就支持對(duì)通信雙方做身份校檢, 安全性高。 傳統(tǒng)的進(jìn)程通信方式對(duì)于通信雙方的身份并沒(méi)有做出嚴(yán)格的驗(yàn)證, 只有在上層協(xié)議上進(jìn)行架設(shè);比如Socket通信ip地址是客戶(hù)端手動(dòng)填入的, 都可以進(jìn)行偽造 2、性能:socket作為一款通用接口, 其傳輸效率低, 開(kāi)銷(xiāo)大, 主要用在跨網(wǎng)絡(luò)的進(jìn)程間通信和本機(jī)上進(jìn)程間的低速通信, Binder其實(shí)通過(guò)Binder驅(qū)動(dòng)在內(nèi)核區(qū)域進(jìn)行了數(shù)據(jù)的傳輸, 性能高 如何實(shí)現(xiàn)一個(gè)Binder通訊? 1、在項(xiàng)目中新建一個(gè)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、創(chuàng)建一個(gè)在獨(dú)立進(jìn)程的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、另外一個(gè)進(jìn)程, 啟用遠(yuǎn)程的Service, 并調(diào)用接口方法, 進(jìn)行通訊 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) { } };} 只能說(shuō)so easy。 Android提供了優(yōu)秀的API, 使得我們可以很方便的通過(guò)AIDL實(shí)現(xiàn)進(jìn)程間的通訊。 貌似我們實(shí)現(xiàn)了進(jìn)程間通訊, 但是連Binder的身影都沒(méi)看到, 這也就是上面的Binder對(duì)很多童鞋都很陌生的原因。 Binder的原理 我們定義了AIDI后, 默認(rèn)系統(tǒng)都會(huì)生成一個(gè)集成了IInterface的接口的類(lèi), eclipse默認(rèn)是在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;} 這個(gè)類(lèi)也就是我們?cè)趏nServiceConnected中使用的接口。 在這個(gè)IBookManager中我們有幾個(gè)關(guān)鍵的類(lèi)Stub、Proxy, 也見(jiàn)到了久違的Binder。 那么糾結(jié)Binder是怎么樣來(lái)進(jìn)行間通訊的呢?下面我們先通過(guò)一個(gè)示例圖來(lái)簡(jiǎn)單描述一下該流程。 IInterface結(jié)構(gòu)分析 首先變量DEOR定義了接口和對(duì)應(yīng)方法的唯一標(biāo)示。 因?yàn)锽inder其實(shí)是一種底層的通訊方式, Google工程師將Binder包裝成了一種對(duì)象的引用。 所以這里的標(biāo)識(shí)是告訴底層的Binder驅(qū)動(dòng), 我的Binder引用標(biāo)識(shí), 對(duì)應(yīng)的方法標(biāo)識(shí)。 這樣Binder驅(qū)動(dòng)才能在進(jìn)程間進(jìn)行裝換。 Proxy:實(shí)現(xiàn)了IBookManager接口, 這個(gè)代理類(lèi)是往Binder驅(qū)動(dòng)里面寫(xiě)數(shù)據(jù), 通過(guò)調(diào)用Binder的transact方法往Binder驅(qū)動(dòng)寫(xiě)Parcel數(shù)據(jù)。 可以理解為告訴Binder驅(qū)動(dòng)我們要調(diào)用什么方法 mRemote.transact( Stub.TRANSACTION_addBook, _ data, _ reply, 0); Stub:繼承了Binder, 實(shí)現(xiàn)了IBookManager接口。 Binder驅(qū)動(dòng)調(diào)用遠(yuǎn)程方法成功后, 要回調(diào)告訴執(zhí)行的結(jié)果。 通過(guò)回調(diào)onTransact方法。 將Binder驅(qū)動(dòng)中的Parcel數(shù)據(jù)轉(zhuǎn)換為我們的回調(diào)數(shù)據(jù)。 IBinder引用是什么時(shí)候注冊(cè)到了Binder驅(qū)動(dòng)中呢? 我們知道Stub繼承了Binder, 那么當(dāng)Stub實(shí)例化的時(shí)候, 這個(gè)時(shí)候Binder無(wú)參構(gòu)造被調(diào)用, 執(zhí)行了一個(gè)native 的init方法。 這個(gè)時(shí)候向Binder驅(qū)動(dòng)注冊(cè)了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驅(qū)動(dòng)寫(xiě)數(shù)據(jù))最后調(diào)用為BinderProxy代理類(lèi)的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驅(qū)動(dòng)回調(diào)數(shù)據(jù)) 根據(jù)定義的常量標(biāo)識(shí), 解析Parcel數(shù)據(jù), 回調(diào)本地方法 @ 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應(yīng)用層源碼實(shí)現(xiàn)流程分析 我們了解了Binder通訊的一些基礎(chǔ)原理后, 通過(guò)應(yīng)用層的調(diào)用來(lái)追蹤整個(gè)執(zhí)行的流程。 下面我們通過(guò)一個(gè)表格索引來(lái)描述Binder通訊的結(jié)構(gòu)
1、本文對(duì)Bidner做了一些整體的介紹, 主要是基于應(yīng)用層的流程進(jìn)行分析, 如果要徹底搞清楚Bidner, 可能還需閱讀Binder驅(qū)動(dòng)的源碼及Bidner的協(xié)議等 2、Binder在Android體系中, 有著非常重要的地位, 是核心的IPC方式。 如果希望學(xué)習(xí)Android源碼, Binder是一道需要越過(guò)去的坎。 手機(jī)APP這些安裝在手機(jī)里面豐富多彩的軟件,讓我們的生活更加方便和精彩。游戲、社交、購(gòu)物、視頻、音樂(lè)、學(xué)習(xí)......我們可以隨時(shí)隨地,隨處進(jìn)行。 |
溫馨提示:喜歡本站的話(huà),請(qǐng)收藏一下本站!