servic

一.上次的兩個問題:

  1. 在BindService爲什麼不調用onServiceDisConnection()

類ServiceConnection中的onServiceDisconnected()方法在正常情況下是不被調用的,它的調用時機是當Service服務被異外銷燬時,例如內存的資源不足時這個方法才被自動調用。

Android系統在同service的連接意外丟失時調用這個.比如當service崩潰了或被強殺了.當客戶端解除綁定時,這個方法不會被調用.

broadcastReceiver只能通過startService啓動Service,因爲廣播本身生命週期很短,bind的話沒有意義

  1. bindService(Intent,ServiceConnection對象,常量)

第一個參數:Intent指示對應的Service對象

第二個參數:實現了 ServiceConnection接口的對象

第三個參數:Flags

在進行服務綁定時,其標誌位可以爲BIND_AUTO_CREATE、BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND等。其中BIND_AUTO_CREATE表示當收到綁定請求時,如果服務尚未創建,則即刻創建,在系統內存不足,需要先銷燬優先級組件來釋放內存,且只有駐留該服務的進程成爲被銷燬對象時,服務纔可被銷燬;BIND_DEBUG_UNBIND通常用於調試場景中判斷綁定的服務是否正確,但其會引起內存泄漏,因此非調試目的不建議使用;BIND_NOT_FOREGROUND表示系統將阻止駐留該服務的進程具有前臺優先級,僅在後臺運行,該標誌位在Froyo中引入。

常量有:

Context. BIND_AUTO_CREATE:這樣就會在service不存在時創建一個

automatically create the service as long

 * as the binding exists.  Note that while this will create the service,

 * its {@link android.app.Service#onStartCommand}

 * method will still only be called due toan

 * explicit call to {@link #startService}.  Even without that, though,

 * this still provides you with access tothe service object while the

 * service iscreated.

Context. BIND_DEBUG_UNBIND

Flag for {@link #bindService}: include debugging help for mismatched

 * calls to unbind.  When this flag is set, thecallstackof the following

 * {@link #unbindService} call is retained, tobe printed if a later

 * incorrect unbind call is made.  Note that doing this requires retaining

 * information about the binding that wasmade for the lifetime of theapp,

 * resulting ina leak -- this shouldonly be used for debugging.

Context. BIND_NOT_FOREGROUND

Flag for {@link #bindService}: don’t allow this binding to raise

 * the target service's process to theforeground scheduling priority.

 * It will still be raised to at least thesame memory priority

 * as the client (so that its process willnot bekillable in any

 * situation where the client is notkillable),but for CPU scheduling

 * purposes it may be left in thebackground.  This only has an impact

 * in the situation where the bindingclient is a foreground process

 * and thetarget service is in a background process.

Context. BIND_ABOVE_CLIENT

Flag for {@link #bindService}: indicates that the client application

 * binding to this service considers theservice to be more important than

 * the app itself.  When set, the platform will try to have theout of

 * memory kill the app before itkills the service it is bound to, though

 * this is notguaranteed to be the case.

/**

 * Flag for {@link #bindService}: allow the processhosting the bound

 * service to go through its normal memorymanagement.  It will be

 * treated more like a running service,allowing the system to

 * (temporarily) expunge the process if lowon memory or for some other

 * whim it may have, and being moreaggressive about making it a candidate

 * to be killed (and restarted) if runningfor a long time.

 */

publicstaticfinalintBIND_ALLOW_OOM_MANAGEMENT = 0x0010;

/**

 * Flag for {@link #bindService}: don't impact thescheduling or

 * memory management priority of the targetservice's hosting process.

 * Allows the service's process to bemanaged on the background LRU list

 * just like a regular application processin the background.

 */

publicstaticfinalintBIND_WAIVE_PRIORITY = 0x0020;

/**

 * Flag for {@link #bindService}: this service is veryimportant to

 * the client, so should be brought to theforeground process level

 * when the client is.  Normally a process can only be raised to the

 * visibility level by a client, even ifthat client is in the foreground.

 */

publicstaticfinalintBIND_IMPORTANT= 0x0040;

/**

 * Flag for {@link #bindService}: If binding from anactivity, allow the

 * target service's process importance tobe raised based on whether the

 * activity is visible to the user,regardless whether another flag is

 * used to reduce the amount that theclient process's overall importance

 * is used to impact it.

 */

publicstaticfinalintBIND_ADJUST_WITH_ACTIVITY = 0x0080;

/**

 * Flag for {@link #bindService}: Don't consider thebound service to be

 * visible, even if the caller is visible.

 * @hide

 */

publicstaticfinalintBIND_NOT_VISIBLE = 0x40000000;

========================================================================

二.混合使用startService和bindService方法(例子:ServiceFixDemo)

Service生命週期問題:onCreateonStartonDestroyonBind

1). StartService被啓動的服務的生命週期:如果一個Service被某個Activity調用 Context.startService方法啓動,那麼不管是否有Activity使用bindService綁定或unbindService解除綁定到該Service,該Service都在後臺運行。如果一個Service被startService方法多次啓動,那麼onCreate方法只會調用一次,onStart將會被調用多次(對應調用startService的次數),並且系統只會創建Service的一個實例(因此你應該知道只需要一次stopService調用)。該Service將會一直在後臺運行,而不管對應程序的Activity是否在運行,直到被調用stopService,或自身的stopSelf方法。當然如果系統資源不足,android系統也可能結束服務。【實現啓動服務,服務可以在後臺長時間運行,不能和服務通信】

2). bindService被綁定的服務的生命週期:如果一個Service被某個Activity調用 Context.bindService方法綁定啓動,不管調用 bindService 調用幾次,onCreate方法都只會調用一次,同時onStart方法始終不會被調用。當連接建立之後,Service將會一直運行,除非調用Context.unbindService斷開連接或者之前調用bindService 的 Context不存在了(如Activity被finish的時候),系統將會自動停止Service,對應onDestroy將被調用。

【實現啓動服務,服務與其啓動組件有依賴關係,實現了和服務通信–Binder】

3).混合使用–被啓動又被綁定的服務的生命週期:如果一個Service又被啓動又被綁定,則該Service將會一直在後臺運行。並且不管如何調用,onCreate始終只會調用一次,對應startService調用多少次,Service的onStart便會調用多少次。調用unbindService將不會停止Service,而必須調用stopService 或 Service的 stopSelf來停止服務

bindService和startService混合使用

a.在bind的Activity退出的時候,service會執行unBind()方法而不執行OnDestroy()方法,因爲有startService方法調用過,所有Activity與Service解除綁定後會有一個與調用者沒有關聯的Service存在。

b.如果先bindService,再startService,再調用Context.stopService()

Service的OnDestroy()方法不會立即執行,因爲有一個與Service綁定的Activity,但是在Activity退出的時候,會執行OnDestroy,如果要立即執行stopService,就得先解除綁定。[否則應用會報錯]

C.如果先執行startService,再執行bindService,結果是一樣的。

【實現啓動服務,服務可以在後臺長時間運行,服務與其啓動組件有依賴關係,實現了和服務通信–Binder】

4). 當服務被停止時清除服務:當一個Service被終止(1、調用stopService;2、調用stopSelf;3、不再有綁定的連接(沒有被啓動))時,onDestroy方法將會被調用,在這裏你應當做一些清除工作,如停止在Service中創建並運行的線程。

特別注意:

1、你應當知道在調用bindService綁定到Service的時候,你就應當保證在某處調用 unbindService解除綁定(儘管 Activity被 finish 的時候綁定會自動解除,並且Service會自動停止);

2、你應當注意使用 startService啓動服務之後,一定要使用 stopService停止服務,不管你是否使用bindService;

3、同時使用 startService與 bindService 要注意到,Service 的終止,需要unbindService與stopService同時調用,才能終止Service,不管 startService與 bindService 的調用順序,如果先調用 unbindService此時服務不會自動終止,再調用 stopService之後服務纔會停止,如果先調用 stopService此時服務也不會終止,而再調用 unbindService或者之前調用 bindService的 Context 不存在了(如Activity 被 finish 的時候)之後服務纔會自動停止;

4、Service.onBind如果返回null,則調用bindService 會啓動 Service,但不會連接上 Service,因此 ServiceConnection.onServiceConnected不會被調用,但你任然需要使用unbindService函數斷開它,這樣Service 纔會停止。

5.如果service已經啓動並且接受綁定,那麼當系統調用你的onUnbind()方法,你可以選擇返回true表示你想在客戶端下一次綁定到service時接受一個onRebind()的調用(而不是一個OnBind()的調用),OnRebind()返回void,但是客戶端依然在它的onServiceConnectionted()回調中接收到IBinder。【例子:ServiceFixDemo】

6.問題:如果在一個Activity的onCreate方法中,先bindService(),再startService(),退出這個Activity時,會執行onUnBind,但是再次進入這個Activity的時候,爲什麼不執行onBind方法了?【例子:ServiceFixTwoDemo】

只有在這個Service銷燬後(執行onDestory),再進這個Activity纔會執行onBind。

還有就是當有兩個客戶端時,在第一個客戶端startServie啓動服務再bindService綁定服務(啓動時會調用onBind()),這時跳到第二個客戶端裏,再客戶端startServie啓動服務再bindService綁定服務,啓動時不會調用用onBind()了(因爲之前客戶端已經啓動後沒有onDestory()銷燬Service,所以再客戶端第二次綁定服務時,只會返回IBinder對象給onServiceConnected()),而且要注意的是:當第一個服務啓動並綁定一個服務時,再跳去第二個服務端啓動並綁定這個服務時,第二個服務端再解綁時,不會調用onUnbind(),只有回到第一個客戶端時,解綁這是纔會調用onUnbind(),順序反過來結果是一樣的。得出一個結論是:當一個服務沒被onDestory()銷燬之前,只有第一個啓動它的客戶端能調用它的onBind()和onUnbind()。

7、當在旋轉手機屏幕的時候,當手機屏幕在“橫”“豎”變換時,此時如果你的Activity 如果會自動旋轉的話,旋轉其實是 Activity的重新創建,因此旋轉之前的使用 bindService建立的連接便會斷開(Context不存在了),對應服務的生命週期與上述相同。

8、在 sdk 2.0及其以後的版本中,對應的 onStart已經被否決變爲了 onStartCommand,不過之前的 onStart任然有效。這意味着,如果你開發的應用程序用的 sdk爲 2.0 及其以後的版本,那麼你應當使用 onStartCommand而不是 onStart

三.服務按運行類型分類—-前臺服務【創建前臺服務【ServiceForeGroundDemo】

類別

區別

應用

前臺服務

會在通知一欄顯示 ONGOING的 Notification,

當服務被終止的時候,通知一欄的 Notification也會消失,這樣對於用戶有一定的通知作用。常見的如音樂播放服務。

後臺服務

默認的服務即爲後臺服務,即不會在通知一欄顯示 ONGOING的 Notification。

當服務被終止的時候,用戶是看不到效果的。某些不需要運行或終止提示的服務,如天氣更新,日期同步,郵件同步等。

後臺服務我們可以自己創建 ONGOING的 Notification這樣就成爲前臺服務嗎?答案是否定的,前臺服務是在做了上述工作之後需要調用 startForeground( android2.0及其以後版本 )或 setForeground(android 2.0以前的版本)使服務成爲前臺服務。這樣做的好處在於,當服務被外部強制終止掉的時候,ONGOING的 Notification也被移除掉。

前臺服務好處:系統在運行後臺服務的時候,發現在手機休眠一段時間後(1-2小時),後臺運行的服務被強行kill掉,有可能是系統回收內存的一種機制,要想避免這種情況可以通過startForeground讓服務前臺運行,當stopService的時候通過stopForeground去掉。

前臺服務只是提高了服務的優先級。當然並不能保證你得 Service 永遠不被殺掉,只是提高了他的優先級。【ForegroundService項目使用了反射機制來啓動前臺服務】

Public static intONGOING_NOTIFICATION=1;

Notification notification=newNotification(R.drawable.icon, getText(R.string.ticker_text),System.currentTimeMillis());

Intent notificationIntent=newIntent(this,ExampleActivity.class);

PendingIntent pendingIntent=PendingIntent.getActivity(this,0,notificationIntent,0);

notification.setLatestEventInfo(this, getText(R.string.notification_title),

   getText(R.string.notification_message), pendingIntent);

startForeground(ONGOING_NOTIFICATION, notification);

方法解釋:

爲了使服務在前臺執行,需要調用 startForeground(intnoticationID,Notication notication).這個參數有兩個參數,一個是通知的標示,第二個是顯示在狀態欄中的通知。stopForeground(boolean).是否移除狀態欄中的Notication。這個方法不能停止服務。但是,當這個服務正在運行的時候去停止服務(沒有調用stopForeground()方法),這個Notication任然會被移除。【setForeground(boolean)只是簡單的改變service的狀態爲background】

四.BindService客戶端和服務端通信的幾種方法總結:

.創建BindService

如果客戶端通過bindService()方法綁定服務,此時,客戶端必須提供ServiceConnection接口的實現類,該類的功能:監視客戶端和服務的連接。當Android系統創建客戶端與服務直接的連接,它調用ServiceConnection接口的OnServiceConnection()方法,來發送客戶端用來與服務通信的IBinder對象。

在實現綁定服務時,最重要的方法是OnBinde()回調方法返回的接口,有三種方法:

方法一:繼承Binder類(支持跨進程)

如果服務對應用程序私有並且與客戶端運行在相同的進程中,則應該繼承Binder類來創建接口,並且從onBind()方法返回其一個實例。客戶端接收到Binder對象並且用其來訪問Binder類實現類或者Service類中的公共方法。【支持跨進程原因:客戶端能夠轉型返回對象並且適當的調用其方法】

方法二:使用Messenger

Messenger:信使

官方文檔解釋:它引用了一個Handler對象,以便others能夠向它發送消息(使用mMessenger.send(Messagemsg)方法)。該類允許跨進程間基於Message的通信(即兩個進程間可以通過Message進行通信),在服務端使用Handler創建一個Messenger,客戶端持有這個Messenger就可以與服務端通信了。

以前我們使用Handler+Message的方式進行通信,都是在同一個進程中,從線程持有一個主線程的Handler對象,並向主線程發送消息。

而Android既然可以使用bindler機制進行跨進行通信,所以我們當然可以將Handler與bindler結合起來進行跨進程發送消息。

查看API就可以發現,Messenger就是這種方式的實現。

如果需要接口跨進程工作,則可以使用Messenger類來創建接口。此時,服務定義的Handler對象來響應不同類型的Message對象。Handler是Messenger的基石,能與客戶端分享IBinder,允許客戶端使用Message對象向服務發送命令。此外,客戶端能定義自己的Message對象,這樣服務能發送回消息。

使用Messenger是執行進程通信(IPC)最簡單的方式。在單個線程中,Messenger類將所有的請求隊列化,這樣服務每次收到一個請求,這樣就不必設計服務爲線程安全。

它引用一個Handler對象,以便others能夠像它發送消息(mMessenger.send(Messagemsg)方法)。該類允許進程間基於Message的通信(即連個進程間的通信)。在服務端使用Handler創建一個Messenger,客戶端持有這個Messenger就可以通信了。

Messenger實現方法:只有客戶端向服務端發送消息,單向的【Demo4】

  1. 遠程服務端通過:Messenger mMessenger=newMessenger(mHandler);創建一個信使對象

  2. 客戶端通過使用bindService()請求連接連接遠程

  3. 遠程OnBind()方法返回一個binder對象:mMessenger.getBinder();

  4. 客戶端使用遠程返回的的binder得到一個信使(即得到遠程信使)

Public voidonServiceConnection(ComponentName name,IBinder service){

rMessenger=new Messenger(service);

}

這裏new了一個Messenger對象,其實現源碼:

Messenger對象
privatefinal IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public IBinder getBinder() {
return mTarget.asBinder();
}
Handler對象
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
MessengerImpl對象
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
Handler.this.sendMessage(msg);
}
}

當我們調用mMessenger.getBinder()這個方法時,底層會通過mTarget對象asBinder()方法返回binder對象,而mTarget對象是我們在創建Messenger對象時通過傳入其中的handler對象的getIMessenger()獲取的,在Handler對象的getIMessenger()裏,創建了一個MessengerImpl對象,它實現了 IMessenger.Stub由此可知Messenger也是通過aidl實現進程間通信的,mTarget=Messengerimpl,發現它的mTarget是通過aidl得到的,實際就是遠程創建的那個

  1. 客戶端可以使用這個遠程信使對象向遠程發送消息:rMessenger.send(msg);

遠程服務端的Handler對象就能收到消息了,然後可以調用handlerMessage(Message msg)方法中進行處理。【該Handler對象是第一步服務端創建Messenger是用的參數mHandler】

實現雙向傳遞消息方法:

修改第5步:

//客戶端的對象,服務端可以通過此對象發送消息到客戶端

MessengermClientMessenger=new Messenger(new ClientHandler()); //創建客戶端信使

在rMessenger.send(msg)之前通過:msg.repleyTo=mClientMessenger將自己的信使設置到消息中,這樣服務端接收到消息時同時得到客戶端的信使對象了,然後服務端可以在自己的Handler對象的hanlerMessage方法中接收客戶端信使對象:MessengerclientMessenger=msg.replyTo得到客戶端的信使對象,並向它發送消息clientMessenger.send(message);

即完成了從服務端向客戶端發送消息的功能,這樣客戶端可以在自己的Handler對象的hanlerMessage方法中接收服務端發送的message進行處理

方法三:aidl(後續講)

五.使用哪種方法啓動服務

在什麼情況下使用startService 或 bindService或同時使用startService和 bindService?

a.如果你只是想要啓動一個後臺服務長期進行某項任務那麼使用 startService便可以了。

b.如果你想要與正在運行的 Service取得聯繫,那麼有兩種方法,一種是使用 broadcast,另外是使用bindService,前者的缺點是如果交流較爲頻繁,容易造成性能上的問題,並且 BroadcastReceiver本身執行代碼的時間是很短的(也許執行到一半,後面的代碼便不會執行),而後者則沒有這些問題,因此我們肯定選擇使用 bindService(這個時候你便同時在使用 startService和bindService 了,這在 Activity中更新 Service的某些運行狀態是相當有用的)。

C.另外如果你的服務只是公開一個遠程接口,供連接上的客服端(android的 Service 是C/S架構)遠程調用執行方法。這個時候你可以不讓服務一開始就運行,而只用 bindService,這樣在第一次bindService 的時候纔會創建服務的實例運行它,這會節約很多系統資源,特別是如果你的服務是Remote Service,那麼該效果會越明顯(當然在 Service創建的時候會花去一定時間,你應當注意到這點)。

六.如何防止Android應用中的Service被系統回收?
對於Service被系統回收,一般做法是通過提高優先級可以解決,在AndroidManifest.xml文件中對於intent-filter可以通過android:priority= “1000”這個屬性設置最高優先級,1000是最高值,如果數字越小則優先級越低,同時實用於廣播,推薦大家如果你的應用很重要,可以考慮通過系統常用intent action來觸發。

另外一種實現方法:上面已經講了—-前臺服務

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章