本文系343315623 devilived原創(chuàng )整理,轉載分享請注明出處(http://hi.baidu.com/canghaiyisu123/blog/item/22e3f9ed78eadc3563d09f2e.html)。
1.首先理解service的作用和生命周期, 由于activity如果切換,那么他就不再運行,那么我們想在玩游戲的時(shí)候聽(tīng)播放器中的音樂(lè ),activity就應運而生了,這是最常見(jiàn)的一種場(chǎng)景,同時(shí)service由于它的優(yōu)先級比較高,不容易被回收,而且是獨立進(jìn)程,不會(huì )阻塞UI線(xiàn)程,因此,可以用來(lái)處理一些比較費時(shí)的任務(wù)。 service起于startService(),終于stopService,如果沒(méi)有調用stopService,那么,即使調用者結束了,該service也一直存在。 也可以通過(guò)bindService來(lái)綁定service,unBindService分開(kāi)并結束service。如果bind的時(shí)候沒(méi)有啟動(dòng)service,那么它會(huì )調用service的create方法啟動(dòng)。 多個(gè)程序可同時(shí)bind同一個(gè)service,只有他們都unbind了,這個(gè)service就會(huì )自動(dòng)結束。如果有部分unbinder的時(shí)候調用stopservice,這個(gè)stop也會(huì )等到它們全部結束的時(shí)候才真的結束。
2.service的種類(lèi), 由于adroid的獨特的線(xiàn)程模型,service被同一個(gè)apk調用和不同apk調用原理是不同的,因此分成以下兩種: (1). 本地服務(wù)(Local Service):說(shuō)白了就是在同一個(gè)apk內被調用。 (2). 遠程服務(wù)(Remote Sercie):被另外一個(gè)apk調用。
3.涉及到的內容: 由于android的進(jìn)程模型,不同的apk不能共用數據,因此如果有需要的話(huà)需要通過(guò)進(jìn)程間通訊完成。 進(jìn)程間通訊(ipc)涉及到三個(gè)部分:客戶(hù)端(調用方),傳遞數據,服務(wù)端(被調用方)。 android的數據傳遞類(lèi)似于RPC過(guò)程,采用aidl的方式傳遞,考慮到效率的原因,沒(méi)有用java內置的serializable,而是采用原始數據拆分組裝的parcel方式。
4.場(chǎng)景假設: 一個(gè)service提供產(chǎn)生Student數據的功能,作為服務(wù)端,一個(gè)apk中的activity想獲取另一個(gè)apk中的一個(gè)學(xué)生。根據上邊的分析,service即使服務(wù)端,activity即使客戶(hù)端,student是要傳輸的數據,要被包裝成pacel傳遞。
5.根據android思想,理想的傳遞過(guò)程: student提供拆分成parcel的writeToParcel()方法和根據Parcel組裝的構造函數Student(Pacel p); 一個(gè)bissiness Service(bizSvc)用來(lái)實(shí)現邏輯功能。 一個(gè)android service(adSvc)調用bizService提供功能。 一個(gè)MyBinder負責service描述符與bizService的映射(本地服務(wù)時(shí)供queryLocalInterface查找)和與客戶(hù)端交互。(如果被本地調用,就查找到相應的service直接操作,不用遠程通訊,如果被遠程調用,就得負責與遠程通訊) 一個(gè)Proxy負責在客戶(hù)端提供transact()方法發(fā)送數據。
具體的調用過(guò)程如下: 調用的時(shí)候,客戶(hù)端首先調用bindService(new Intent ("abc"), serviceConnection,Context.BIND_AUTO_CREATE);激活serviceConnection的onServiceConnected方法,在此方法中獲取到一個(gè)binder(注:clientBinder,系統給我們的一個(gè)與遠程進(jìn)行通信的binder,不是我們剛才自己實(shí)現的),此binder能夠查找系統中注冊的service,如果沒(méi)有查找到該Service,那么可認定該service是從其他apk獲得的,就創(chuàng )建一個(gè)此service的靜態(tài)代理類(lèi)Proxy,否則,就把這個(gè)service返回,供客戶(hù)端調用。
服務(wù)端收到這個(gè)Intent后,激活adSvc的onBind方法,創(chuàng )建一個(gè)MyBinder返回。之后,這個(gè)MyBinder負責與客戶(hù)端的Proxy通信。
之后,客戶(hù)端要調用service的方法,可直接調用(本地)或者通過(guò)代理調用(遠程),這個(gè)Proxy調用clientBinder的transact方法,參數為要掉用的方法(TRANSACRION_XX),發(fā)送的參數(_data),接受的返回值(_reply),把消息傳送給服務(wù)端。 服務(wù)端收到消息后,調用MyBinder的onTransac方法,根據Proxy傳遞過(guò)來(lái)參數,調用bizService不同的方法,并把產(chǎn)生的值組裝成Parcel發(fā)送回去。之后客戶(hù)端Proxy會(huì )自動(dòng)調用sudent的相關(guān)方法,把數據重新組裝,進(jìn)行下一步處理。
理想的代碼如下(這是理想的代碼,經(jīng)過(guò)拆分,但是不符合android的aidl規范,不能運行,見(jiàn)下文分析): 客戶(hù)端代碼: final IMyBizService bizSvc; bindService(new Intent("abc"),new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder clientBinder) { if ((binder == null)) { return null; } IInterface iin = clientBinder.queryLocalInterface(DESCRIPTOR);//如果是查找本地有,就直接返回 if (((iin != null) && (iin instanceof IMyService))) { return ((IMyService) iin); } return new Proxy(obj);//否則采用遠程代理
} }, Context.BIND_AUTO_CREATE);
Proxy.java: public class Proxy implements IMyBizService { private IBinder mRemote; private String description;
public Proxy(IBinder binder,String description) { super(); this.mRemote=binder; this.description=description; }
@Override public IBinder asBinder() { return mRemote; }
public Student getStudent() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); Student _result; try { _data.writeInterfaceToken(description); mRemote.transact(MyBinder.TRANSACTION_getStudent, _data,_reply, 0);//發(fā)送參數給服務(wù)端 _reply.readException(); if ((0 != _reply.readInt())) { _result = new Student(_reply);//接受參數并返回 } else { _result = null; } } finally { _reply.recycle(); _data.recycle(); } return _result; }
}
服務(wù)端代碼: MyService.java: public class MyService extends Service { @Override public IBinder onBind(Intent intent) { MyBizServiceImpl svc=new MyBizServiceImpl(); MyBinder binder= new MyBinder(new MyBizServiceImpl(),intent.getAction()); svc.setBinder(binder);//(此處有點(diǎn)繞,binder方法需要bizService作為參數實(shí)現綁定和調用,而bizSvc需要這個(gè)Binder作為參數,用來(lái)實(shí)現asBind方法,形成了一個(gè)環(huán)); return binder; } }
MyBinder.java: public class MyBinder extends Binder { static final int TRANSACTION_getStudent = (IBinder.FIRST_CALL_TRANSACTION + 1); private IInterface intf; private String descripter;
public MyBinder(IInterface intf, String descripter) { super(); this.intf=intf; this.descripter=descripter; this.attachInterface(intf, descripter); }
@Override public boolean onTransact(int code, Parcel data,Parcel reply, int flags)throws RemoteException { IMyBizService myService=(IMyBizService) this.intf; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descripter); return true; } case TRANSACTION_getStudent: { data.enforceInterface(descripter); Student _result = myService.getStudent(); reply.writeNoException(); if ((_result != null)) { reply.writeInt(1); _result.writeToParcel(reply,Parcelable.PARCELABLE_WRITE_RETURN_VALUE);//發(fā)送數據給客戶(hù)端 } else { reply.writeInt(0); } return true; } } return super.onTransact(code, data, reply, flags); } }
MyBizService.java: public class MyBizServiceImpl implements IMyBizService { private IBinder binder; public void setBinder(IBinder binder){ this.binder=binder; } @Override public IBinder asBinder() { return binder; }
@Override public Student getStudent() throws RemoteException { Student st = new Student(); st.setName("devil");
return st; }
}
5 aidl對這個(gè)機制的改進(jìn): 第4步的代碼不能執行,只能作為理解原理和閱讀代碼用。因為為了安全和效率,aidl的關(guān)鍵類(lèi)自動(dòng)生成,不能修改,同時(shí)android對aidl的保護也使得需要一些額外的檢查。 對比以上代碼,android自動(dòng)生成的代碼有以下幾個(gè)方面不同: 1).MyBinder,IMyBizService,和Proxy通通根據IMyBizService.aidl文件自動(dòng)生成為IMyBizService.java文件。具體關(guān)系如下: MyBinder和IMyBizService被合并成一個(gè)Stub類(lèi)作為IMyBizService的內部類(lèi)(直接導致this被濫用,但同時(shí)實(shí)現了這兩個(gè),那么相關(guān)方法直接用this就行了,不用形成前文所說(shuō)的環(huán)了)。這個(gè)類(lèi)負責接與客戶(hù)端交互。而且還有一個(gè)額外靜態(tài)方法asInterface供客戶(hù)端在引起connection的onConnected時(shí)調用,實(shí)現根據傳入的clientBinder判斷本次調用是本地調用還是遠程調用,從而確定是否采用Proxy,這樣這個(gè)方法既然已經(jīng)實(shí)現了,那么我們直接用就行了,無(wú)須再寫(xiě)一遍。具體IMyBizService的實(shí)現,只需繼承Stub類(lèi)即可。這樣,只需在adService中創(chuàng )建MyBinder的時(shí)候,也就同時(shí)創(chuàng )建了MyBizService. Proxy作為Stub的內部類(lèi)(真糾結。。),被asInterface調用,從而進(jìn)一步被客戶(hù)端調用。 2).由于自動(dòng)生成的IMyBizService.java中的Stub的onTransact利用了Student類(lèi)中的一個(gè)CREATE內部域作為轉換器,我們必須在Student類(lèi)中添加并實(shí)現這個(gè)域,注意:域名必須是CREATE大寫(xiě)。(這個(gè)實(shí)現有點(diǎn)囧,可能是因為在自動(dòng)生成文件的時(shí)候無(wú)法確定這個(gè)轉換方法的寫(xiě)法)。
6 這個(gè)模型一些特例 1).如果本service只作為本地調用,可無(wú)須aidl相關(guān)操作,服務(wù)端的binder直接像stub那樣,即使binder又是service,這樣,調用方(如Activity)由于與該service在同一個(gè)apk中,ServiceConnection中獲取的clientBind就是service返回的那個(gè)myBinder,這樣的話(huà),直接強轉即可得到IMyBizService.不用調用queryLocalInterface()方法再查找一次。 2).如果不用額外調用service的其他方法,只是啟動(dòng)一個(gè)service的話(huà),那么直接用startService即可,無(wú)須bind。
7.對這個(gè)模型的感受 這個(gè)模型有點(diǎn)囧,作者本來(lái)想讓碼工少寫(xiě)點(diǎn)代碼,結果理解起來(lái)結構感覺(jué)亂糟糟的,而且沒(méi)有一條主線(xiàn),內部類(lèi)各種相互調,客戶(hù)端服務(wù)端不分,太不給力了。除非使用者之前有良好的rpc功底和代碼運用能力,否則,要實(shí)現自己定制的功能比以前還得小心,要不就是對著(zhù)示例代碼照貓畫(huà)虎,調試起來(lái)就是一種折磨。(一家之言,有理解不對的地方多多提醒,文中示例參考了http://4225953-163-com.javaeye.com/blog/792997一文中的示例)
8.附帶標準aidl實(shí)現的完整過(guò)程: 1)實(shí)現要傳遞的數據結構和aidl(student.java和student.aidl) 2)實(shí)現相關(guān)的service的aidl(IMyBizService.aidl) 2)如果是eclipse,在1,2完成的情況下,會(huì )自動(dòng)在gen目錄下生成IMyBizService.java文件,如果非eclispse,則需要用sdk/platfroms/{version}/tools/aidl工具生成。 3)在myService中集成Stub類(lèi),實(shí)現IMyBizServic的方法,并作為binder在onBind方法中返回。 4)把IBizInterface.java和Student.java考進(jìn)客戶(hù)端工程(aidl文件安不需要,并且包名不能改變),客戶(hù)端調用Stub的asInterface方法獲取到相關(guān)的接口代理 5)利用接口代理調用各種方法 6)對于以上各個(gè)步驟中的具體操作,網(wǎng)上到處都是。 |
聯(lián)系客服