2025-02-09

1.首先理解service的作用和生命周期,
 
由於activity如果切換,那麼他就不再運行,那麼我們想在玩遊戲的時候聽播放器中的音樂,activity就應運而生瞭,這是最常見的一種場景,同時service由於它的優先級比較高,不容易被回收,而且是獨立進程,不會阻塞UI線程,因此,可以用來處理一些比較費時的任務。
 
service起於startService(),終於stopService,如果沒有調用stopService,那麼,即使調用者結束瞭,該service也一直存在。
 
也可以通過bindService來綁定service,unBindService分開並結束service。如果bind的時候沒有啟動service,那麼它會調用service的create方法啟動。
 
多個程序可同時bind同一個service,隻有他們都unbind瞭,這個service就會自動結束。如果有部分unbinder的時候調用stopservice,這個stop也會等到它們全部結束的時候才真的結束。
 
 
 
2.service的種類,
 
由於adroid的獨特的線程模型,service被同一個apk調用和不同apk調用原理是不同的,因此分成以下兩種:
 
(1). 本地服務(Local Service):說白瞭就是在同一個apk內被調用。
 
(2). 遠程服務(Remote Sercie):被另外一個apk調用。
 
 
 
3.涉及到的內容:
 
由於android的進程模型,不同的apk不能共用數據,因此如果有需要的話需要通過進程間通訊完成。
 
進程間通訊(ipc)涉及到三個部分:客戶端(調用方),傳遞數據,服務端(被調用方)。
 
android的數據傳遞類似於RPC過程,采用aidl的方式傳遞,考慮到效率的原因,沒有用java內置的serializable,而是采用原始數據拆分組裝的parcel方式。
 
 
 
4.場景假設:
 
一個service提供產生Student數據的功能,作為服務端,一個apk中的activity想獲取另一個apk中的一個學生。根據上邊的分析,service即使服務端,activity即使客戶端,student是要傳輸的數據,要被包裝成pacel傳遞。
 
 
 
5.根據android思想,理想的傳遞過程:
 
student提供拆分成parcel的writeToParcel()方法和根據Parcel組裝的構造函數Student(Pacel p);
 
一個bissiness Service(bizSvc)用來實現邏輯功能。
 
一個android service(adSvc)調用bizService提供功能。
 
一個MyBinder負責service描述符與bizService的映射(本地服務時供queryLocalInterface查找)和與客戶端交互。(如果被本地調用,就查找到相應的service直接操作,不用遠程通訊,如果被遠程調用,就得負責與遠程通訊)
 
一個Proxy負責在客戶端提供transact()方法發送數據。
 
 
 
具體的調用過程如下:
 
調用的時候,客戶端首先調用bindService(new Intent ("abc"), serviceConnection,Context.BIND_AUTO_CREATE);激活serviceConnection的onServiceConnected方法,在此方法中獲取到一個binder(註:clientBinder,系統給我們的一個與遠程進行通信的binder,不是我們剛才自己實現的),此binder能夠查找系統中註冊的service,如果沒有查找到該Service,那麼可認定該service是從其他apk獲得的,就創建一個此service的靜態代理類Proxy,否則,就把這個service返回,供客戶端調用。
 
 
 
服務端收到這個Intent後,激活adSvc的onBind方法,創建一個MyBinder返回。之後,這個MyBinder負責與客戶端的Proxy通信。
 
 
 
之後,客戶端要調用service的方法,可直接調用(本地)或者通過代理調用(遠程),這個Proxy調用clientBinder的transact方法,參數為要掉用的方法(TRANSACRION_XX),發送的參數(_data),接受的返回值(_reply),把消息傳送給服務端。
 
服務端收到消息後,調用MyBinder的onTransac方法,根據Proxy傳遞過來參數,調用bizService不同的方法,並把產生的值組裝成Parcel發送回去。之後客戶端Proxy會自動調用sudent的相關方法,把數據重新組裝,進行下一步處理。
 
 
 
理想的代碼如下(這是理想的代碼,經過拆分,但是不符合android的aidl規范,不能運行,見下文分析):
 
客戶端代碼:
 
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);//發送參數給服務端
 
_reply.readException();
 
if ((0 != _reply.readInt())) {
 
_result = new Student(_reply);//接受參數並返回
 
} else {
 
_result = null;
 
}
 
} finally {
 
_reply.recycle();
 
_data.recycle();
 
}
 
return _result;
 
}
 
 
 
}
 
 
 
 
 
服務端代碼:
 
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);//(此處有點繞,binder方法需要bizService作為參數實現綁定和調用,而bizSvc需要這個Binder作為參數,用來實現asBind方法,形成瞭一個環);
 
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);//發送數據給客戶端
 
} 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對這個機制的改進:
 
第4步的代碼不能執行,隻能作為理解原理和閱讀代碼用。因為為瞭安全和效率,aidl的關鍵類自動生成,不能修改,同時android對aidl的保護也使得需要一些額外的檢查。
 
對比以上代碼,android自動生成的代碼有以下幾個方面不同:
 
1).MyBinder,IMyBizService,和Proxy通通根據IMyBizService.aidl文件自動生成為IMyBizService.java文件。具體關系如下:
 
MyBinder和IMyBizService被合並成一個Stub類作為IMyBizService的內部類(直接導致this被濫用,但同時實現瞭這兩個,那麼相關方法直接用this就行瞭,不用形成前文所說的環瞭)。這個類負責接與客戶端交互。而且還有一個額外靜態方法asInterface供客戶端在引起connection的onConnected時調用,實現根據傳入的clientBinder判斷本次調用是本地調用還是遠程調用,從而確定是否采用Proxy,這樣這個方法既然已經實現瞭,那麼我們直接用就行瞭,無須再寫一遍。具體IMyBizService的實現,隻需繼承Stub類即可。這樣,隻需在adService中創建MyBinder的時候,也就同時創建瞭MyBizService.
 
Proxy作為Stub的內部類(真糾結。。),被asInterface調用,從而進一步被客戶端調用。
 
2).由於自動生成的IMyBizService.java中的Stub的onTransact利用瞭Student類中的一個CREATE內部域作為轉換器,我們必須在Student類中添加並實現這個域,註意:域名必須是CREATE大寫。(這個實現有點囧,可能是因為在自動生成文件的時候無法確定這個轉換方法的寫法)。
 
 
 
6 這個模型一些特例
 
1).如果本service隻作為本地調用,可無須aidl相關操作,服務端的binder直接像stub那樣,即使binder又是service,這樣,調用方(如Activity)由於與該service在同一個apk中,ServiceConnection中獲取的clientBind就是service返回的那個myBinder,這樣的話,直接強轉即可得到IMyBizService.不用調用queryLocalInterface()方法再查找一次。
 
2).如果不用額外調用service的其他方法,隻是啟動一個service的話,那麼直接用startService即可,無須bind。
 
 
 
7.對這個模型的感受
 
這個模型有點囧,作者本來想讓碼工少寫點代碼,結果理解起來結構感覺亂糟糟的,而且沒有一條主線,內部類各種相互調,客戶端服務端不分,太不給力瞭。除非使用者之前有良好的rpc功底和代碼運用能力,否則,要實現自己定制的功能比以前還得小心,要不就是對著示例代碼照貓畫虎,調試起來就是一種折磨。(一傢之言,有理解不對的地方多多提醒,文中示例參考瞭http://4225953-163-com.javaeye.com/blog/792997一文中的示例)
 
 
 
8.附帶標準aidl實現的完整過程:
 
1)實現要傳遞的數據結構和aidl(student.java和student.aidl)
 
2)實現相關的service的aidl(IMyBizService.aidl)
 
2)如果是eclipse,在1,2完成的情況下,會自動在gen目錄下生成IMyBizService.java文件,如果非eclispse,則需要用sdk/platfroms/{version}/tools/aidl工具生成。
 
3)在myService中集成Stub類,實現IMyBizServic的方法,並作為binder在onBind方法中返回。
 
4)把IBizInterface.java和Student.java考進客戶端工程(aidl文件安不需要,並且包名不能改變),客戶端調用Stub的asInterface方法獲取到相關的接口代理
 
5)利用接口代理調用各種方法
 
6)對於以上各個步驟中的具體操作,網上到處都是。
 
 
人收藏此文章, 關註此文章發表於3個月前, 已有206次閱讀 共 個評論  人收藏此文章
1.首先理解service的作用和生命周期,
 
由於activity如果切換,那麼他就不再運行,那麼我們想在玩遊戲的時候聽播放器中的音樂,activity就應運而生瞭,這是最常見的一種場景,同時service由於它的優先級比較高,不容易被回收,而且是獨立進程,不會阻塞UI線程,因此,可以用來處理一些比較費時的任務。
 
service起於startService(),終於stopService,如果沒有調用stopService,那麼,即使調用者結束瞭,該service也一直存在。
 
也可以通過bindService來綁定service,unBindService分開並結束service。如果bind的時候沒有啟動service,那麼它會調用service的create方法啟動。
 
多個程序可同時bind同一個service,隻有他們都unbind瞭,這個service就會自動結束。如果有部分unbinder的時候調用stopservice,這個stop也會等到它們全部結束的時候才真的結束。
 
 
 
2.service的種類,
 
由於adroid的獨特的線程模型,service被同一個apk調用和不同apk調用原理是不同的,因此分成以下兩種:
 
(1). 本地服務(Local Service):說白瞭就是在同一個apk內被調用。
 
(2). 遠程服務(Remote Sercie):被另外一個apk調用。
 
 
 
3.涉及到的內容:
 
由於android的進程模型,不同的apk不能共用數據,因此如果有需要的話需要通過進程間通訊完成。
 
進程間通訊(ipc)涉及到三個部分:客戶端(調用方),傳遞數據,服務端(被調用方)。
 
android的數據傳遞類似於RPC過程,采用aidl的方式傳遞,考慮到效率的原因,沒有用java內置的serializable,而是采用原始數據拆分組裝的parcel方式。
 
 
 
4.場景假設:
 
一個service提供產生Student數據的功能,作為服務端,一個apk中的activity想獲取另一個apk中的一個學生。根據上邊的分析,service即使服務端,activity即使客戶端,student是要傳輸的數據,要被包裝成pacel傳遞。
 
 
 
5.根據android思想,理想的傳遞過程:
 
student提供拆分成parcel的writeToParcel()方法和根據Parcel組裝的構造函數Student(Pacel p);
 
一個bissiness Service(bizSvc)用來實現邏輯功能。
 
一個android service(adSvc)調用bizService提供功能。
 
一個MyBinder負責service描述符與bizService的映射(本地服務時供queryLocalInterface查找)和與客戶端交互。(如果被本地調用,就查找到相應的service直接操作,不用遠程通訊,如果被遠程調用,就得負責與遠程通訊)
 
一個Proxy負責在客戶端提供transact()方法發送數據。
 
 
 
具體的調用過程如下:
 
調用的時候,客戶端首先調用bindService(new Intent ("abc"), serviceConnection,Context.BIND_AUTO_CREATE);激活serviceConnection的onServiceConnected方法,在此方法中獲取到一個binder(註:clientBinder,系統給我們的一個與遠程進行通信的binder,不是我們剛才自己實現的),此binder能夠查找系統中註冊的service,如果沒有查找到該Service,那麼可認定該service是從其他apk獲得的,就創建一個此service的靜態代理類Proxy,否則,就把這個service返回,供客戶端調用。
 
 
 
服務端收到這個Intent後,激活adSvc的onBind方法,創建一個MyBinder返回。之後,這個MyBinder負責與客戶端的Proxy通信。
 
 
 
之後,客戶端要調用service的方法,可直接調用(本地)或者通過代理調用(遠程),這個Proxy調用clientBinder的transact方法,參數為要掉用的方法(TRANSACRION_XX),發送的參數(_data),接受的返回值(_reply),把消息傳送給服務端。
 
服務端收到消息後,調用MyBinder的onTransac方法,根據Proxy傳遞過來參數,調用bizService不同的方法,並把產生的值組裝成Parcel發送回去。之後客戶端Proxy會自動調用sudent的相關方法,把數據重新組裝,進行下一步處理。
 
 
 
理想的代碼如下(這是理想的代碼,經過拆分,但是不符合android的aidl規范,不能運行,見下文分析):
 
客戶端代碼:
 
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);//發送參數給服務端
 
_reply.readException();
 
if ((0 != _reply.readInt())) {
 
_result = new Student(_reply);//接受參數並返回
 
} else {
 
_result = null;
 
}
 
} finally {
 
_reply.recycle();
 
_data.recycle();
 
}
 
return _result;
 
}
 
 
 
}
 
 
 
 
 
服務端代碼:
 
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);//(此處有點繞,binder方法需要bizService作為參數實現綁定和調用,而bizSvc需要這個Binder作為參數,用來實現asBind方法,形成瞭一個環);
 
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);//發送數據給客戶端
 
} 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對這個機制的改進:
 
第4步的代碼不能執行,隻能作為理解原理和閱讀代碼用。因為為瞭安全和效率,aidl的關鍵類自動生成,不能修改,同時android對aidl的保護也使得需要一些額外的檢查。
 
對比以上代碼,android自動生成的代碼有以下幾個方面不同:
 
1).MyBinder,IMyBizService,和Proxy通通根據IMyBizService.aidl文件自動生成為IMyBizService.java文件。具體關系如下:
 
MyBinder和IMyBizService被合並成一個Stub類作為IMyBizService的內部類(直接導致this被濫用,但同時實現瞭這兩個,那麼相關方法直接用this就行瞭,不用形成前文所說的環瞭)。這個類負責接與客戶端交互。而且還有一個額外靜態方法asInterface供客戶端在引起connection的onConnected時調用,實現根據傳入的clientBinder判斷本次調用是本地調用還是遠程調用,從而確定是否采用Proxy,這樣這個方法既然已經實現瞭,那麼我們直接用就行瞭,無須再寫一遍。具體IMyBizService的實現,隻需繼承Stub類即可。這樣,隻需在adService中創建MyBinder的時候,也就同時創建瞭MyBizService.
 
Proxy作為Stub的內部類(真糾結。。),被asInterface調用,從而進一步被客戶端調用。
 
2).由於自動生成的IMyBizService.java中的Stub的onTransact利用瞭Student類中的一個CREATE內部域作為轉換器,我們必須在Student類中添加並實現這個域,註意:域名必須是CREATE大寫。(這個實現有點囧,可能是因為在自動生成文件的時候無法確定這個轉換方法的寫法)。
 
 
 
6 這個模型一些特例
 
1).如果本service隻作為本地調用,可無須aidl相關操作,服務端的binder直接像stub那樣,即使binder又是service,這樣,調用方(如Activity)由於與該service在同一個apk中,ServiceConnection中獲取的clientBind就是service返回的那個myBinder,這樣的話,直接強轉即可得到IMyBizService.不用調用queryLocalInterface()方法再查找一次。
 
2).如果不用額外調用service的其他方法,隻是啟動一個service的話,那麼直接用startService即可,無須bind。
 
 
 
7.對這個模型的感受
 
這個模型有點囧,作者本來想讓碼工少寫點代碼,結果理解起來結構感覺亂糟糟的,而且沒有一條主線,內部類各種相互調,客戶端服務端不分,太不給力瞭。除非使用者之前有良好的rpc功底和代碼運用能力,否則,要實現自己定制的功能比以前還得小心,要不就是對著示例代碼照貓畫虎,調試起來就是一種折磨。(一傢之言,有理解不對的地方多多提醒,文中示例參考瞭http://4225953-163-com.javaeye.com/blog/792997一文中的示例)
 
 
 
8.附帶標準aidl實現的完整過程:
 
1)實現要傳遞的數據結構和aidl(student.java和student.aidl)
 
2)實現相關的service的aidl(IMyBizService.aidl)
 
2)如果是eclipse,在1,2完成的情況下,會自動在gen目錄下生成IMyBizService.java文件,如果非eclispse,則需要用sdk/platfroms/{version}/tools/aidl工具生成。
 
3)在myService中集成Stub類,實現IMyBizServic的方法,並作為binder在onBind方法中返回。
 
4)把IBizInterface.java和Student.java考進客戶端工程(aidl文件安不需要,並且包名不能改變),客戶端調用Stub的asInterface方法獲取到相關的接口代理
 
5)利用接口代理調用各種方法
 
6)對於以上各個步驟中的具體操作,網上到處都是。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *