1. 程式人生 > >混合使用startService,bindService,以及tartForegroud方法使用總結

混合使用startService,bindService,以及tartForegroud方法使用總結

一.上次的兩個問題:

1.     BindService為什麼不呼叫onServiceDisConnection()

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

Android系統在同service的連線意外丟失時呼叫這個.比如當service崩潰了或被強殺了.當客戶端解除繫結時,這個方法不會被呼叫.

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

2.   bindService(Intent,ServiceConnection物件,常量)

第一個引數:Intent指示對應的Service物件

第二個引數:實現了 ServiceConnection介面的物件

第三個引數:Flags

在進行服務繫結時,其標誌位可以為BIND_AUTO_CREATEBIND_DEBUG_UNBINDBIND_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;

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

二.混合使用startServicebindService方法(例子:ServiceFixDemo

Service生命週期問題:onCreateonStartonDestroyonBind

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

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

實現啟動服務,服務與其啟動元件有依賴關係,實現了和服務通訊--Binder

3).混合使用--被啟動又被繫結的服務的生命週期:如果一個Service又被啟動又被繫結,則該Service將會一直在後臺執行。並且不管如何呼叫,onCreate始終只會呼叫一次,對應startService呼叫多少次,ServiceonStart便會呼叫多少次。呼叫unbindService將不會停止Service,而必須呼叫stopService Service stopSelf來停止服務

bindServicestartService混合使用

a.在bindActivity退出的時候,service會執行unBind()方法而不執行OnDestroy()方法,因為有startService方法呼叫過,所有ActivityService解除繫結後會有一個與呼叫者沒有關聯的Service存在。

b.如果先bindService,再startService,再呼叫Context.stopService()

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

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

實現啟動服務,服務可以在後臺長時間執行,服務與其啟動元件有依賴關係,實現了和服務通訊--Binder

4). 當服務被停止時清除服務:當一個Service被終止(1、呼叫stopService2、呼叫stopSelf3、不再有繫結的連線(沒有被啟動))時,onDestroy方法將會被呼叫,在這裡你應當做一些清除工作,如停止在Service中建立並執行的執行緒。

特別注意:

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

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

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

4Service.onBind如果返回null,則呼叫bindService 會啟動 Service,但不會連線上 Service,因此 ServiceConnection.onServiceConnected不會被呼叫,但你任然需要使用unbindService函式斷開它,這樣Service 才會停止。

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

6.問題:如果在一個ActivityonCreate方法中,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及其以後版本)或 setForegroundandroid 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機制進行跨進行通訊,所以我們當然可以將Handlerbindler結合起來進行跨程序傳送訊息。

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

如果需要介面跨程序工作,則可以使用Messenger類來建立介面。此時,服務定義的Handler物件來響應不同型別的Message物件。HandlerMessenger的基石,能與客戶端分享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得到的,實際就是遠端建立的那個

5.     客戶端可以使用這個遠端信使物件向遠端傳送訊息: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(這個時候你便同時在使用 startServicebindService 了,這在 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來觸發。

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