2013年2月5日 星期二

[Android]Android四大元件之Service的生命週期(三)

(3)Service :
Service就是一段程式,它並沒有UI呈現畫面,但它可以長期的存在系統內執行。舉個多媒體播放器的例子來說:一個媒體播放器可能會有一個以上的activities存在,允許使用者選歌及播放歌曲。但是你可能不能一直用activity來做播放的行為,因為使用者可能會希望可以持續的播放歌曲的同時,他要去看其它的畫面,比如一邊聽歌,一邊逛網頁。
在這種情況下你就必需使用Context.startService()來使程式在背景執行,以保持音樂的持續播放。系統將會一直讓音樂保持播放的狀態,一直到它結束為止。
當它進入背景狀態時,你可以利用Context.bindService()來跟Service進行連結。當你正確的連結後,你就可以透過這個Service開放出來的界面跟它進行溝通。以剛剛放音樂的例子來看,Service應該就會開放出讓你暫停播放,倒轉,快轉等功能囉!






圖 Service的生命週期


Android Service-概念(一)

一.說明
Service本身是個無介面,只單純提供功能讓其他元件(Activity)呼叫使用的元件,如果有需要長時間執行的獨立程序,可以寫成Service元件提供其他人呼叫使用。

Service本身並非一個獨立的Process,也不會脫離Main Thread獨立運作,如果需要做長時間的CPU運算,為了不影響到主程式運作,可以在Service內產生Thread,讓thread幫你作一些工作,Service的子類別IntentService即是一個有自己Thread 的Service,服務給並無直接與使用者互動的介面。
當Service元件被產生時,一定會執行其onCreate(),如果有需要產生Thread,則在這裡去產生新的thread

當Service有設定在Manifest上,就可以被自己與其他的App來做存取,其他App要使用service,需在他自己App的Manifest內的uses-permission作宣告

二.執行Service功能的兩種方式
1.startService
當有元件呼叫Context.startService(),系統會讀取被呼叫的Service(產生instance並執行其onCreate()),然後將client所傳進來的intent帶進Service的onStartCommand(Intent, int, int)內。這service會一直執行,直到有人呼叫Context.stopService()或內部呼叫stopSelf()而停止。

無論Context.startService()被呼叫了多少次,如果要停止Service,只要執行一次Context.stopService(),或在Service內執行stopSelf(),就會被停止。
如果是在Service內使用stopSelf(int),可以保證intent已經有被處理了,才會被停止。

對於已被啟動的Service,如果需要有不同的Service執行模式,可以在override onStartCommand()時,設定不同的回傳值。這回傳值常數已經被定義好在Service Class內

1.START_STICKY:
  當Service在執行時被砍掉後,若沒有新的intent進來,
  Service會停留在started state,但intent資料不會被保留
2.START_NOT_STICKY或START_REDELIVER_INTENT:
  當Service在執行時被砍掉後,若沒有新的intent進來,
  service會離開started  state,若沒有很明確的再啟動,
  將不會產生新的service物件

2.bindService
Client可以利用Context.bindService() 去建立與service持續性的連線,
如果建立連線時,還沒產生Service instance,會自動產生並呼叫執行onCreate。與startService不同的地方是,接下來並不會呼叫onStartCommand

要執行bindService,首先要先建立一個serviceConnection物件,把這conection物件當作參數放到bindService內讓Context與Service建立連線,Context.bindService(Intent service, ServiceConnection conn, int flags)
flags參數預設是Context.BIND_AUTO_CREATE,也就是bind的時候會自動產生service

當連線成功後,會自動呼叫執行這個connection內的onServiceConnected(ComponentName className, IBinder service) function,在這function會接收到由service內的onBinde()所丟出來的Ibinder物件
利用這IBinder物件取得Service物件,就可以直接操作Service內各個public 的method

三.Service Process重要性判斷
系統會盡量保持Service的Process運作,但若需要刪除Process,以下是重要性判斷準則

1.正在執行onCreate(), onStartCommand()或onDestroy()的
  Service會被當成前景執行程序,不會被刪除
2.假若這ervice已經被啟動(started),那這service所在的
  process的重要性,會是在visible process與hide process之間
3.如果service所在的Process上有元件是visible,那這Service
  的重要性就相當於是visible
4.一個已經started的service可以利用
  startForeground(int,Notification)放在比較重要的
  前景執行狀態,在記憶體少的時候比較不會被刪除

當記憶體不足Service被刪除後,它稍後還是會被重新啟動產生出來,但通常重新啟動後,原本的intent並不會被保留,如果需要在重新啟動後,再重發intent給Service,必須在override onStartCommand()時return START_REDELIVER_INTENT讓重新啟動的程序裡的onStartCommand(Intent intent, int flags, int startId)內的flags值=START_FLAG_REDELIVERY



Android Service-幾種應用範例(二)

一.實作要點

1.說明
Service提供兩種的使用方式,如果Client只是透過startService去執行Service的功能,只是把要做的功能寫在onStartCommand內即可,若是Client透過bindService來執行Service,就需要在Service內產生Ibinder物件,讓onBind可以把binder物件傳給Client,讓Client可以由取到的binder物件再取到Service物件,然後就可以呼叫Service上提供的method

2.Activity(使用Service的Client)

1.extends Activity物件
2.實做ServiceConnection(需要bindService時使用)
  實做並產生一個connection instance,在connection內
  override其onServiceConnected(ComponentName className, IBinder service)
  與onServiceDisconnected(ComponentName className)

3.onServiceConnected被自動呼叫(需要bindService時使用)
  在與Service連線成功後,onServiceConnected會被自動呼
  叫並接收到由Service內部onBinder所發送出來的IBinder物件,
  通常會是extends 自Binder
4.取得Service物件(需要bindService時使用)
  將取得的Binder物件呼叫其Class內定義的方法(如getService)
  來取得Service物件,如Service(binder.getService())
5.利用取得的Service呼叫執行中Service上的public method

3.Service(提供服務的元件)

1.extends Service物件
2.設計binder class extends Binder(需要bindService時使用)
  設計一個內部Binder Clas並產生binder instance,在class內
  建立getService function,可以透過這binder取得所在的service
3.overrider onBinder function(需要bindService時使用)
  將已建立的binder instance傳出去
4.override onStartCommand(需要用startService時使用)
  若只用startService啟動Service,只需要override這
  function即可

二.Sample:LocalService 使用startService

1.說明
透過startService呼叫Service會執行Service內部的onStartCommand(),所以

2.Activity

Intent intent = new Intent(this, MusicService.class);
intent.putExtra("songPath","sdcard/mp3/mySong.mp3");
this.startService(intent);
3.Service
@Override
public void onCreate() {    
    super.onCreate();
    mp=new MediaPlayer();    
}
@Override
public void onStart(Intent intent, int startId) {
    super.onStart(intent, startId);
    String songPath=intent.getExtras().getString("songPath");
    try{
        mp.setDataSource(_path);
        mp.prepare();
        mp.start();
    }catch(Exception e){};
}

三.Sample:LocalService 使用bindService

1.說明
透過bind方式要執行Service上的功能,是透過建立連線物件後,再直接對Service做操作,要做的工作就寫成Service內public method 讓人呼叫執行

2.Activity
private MusicService mService;
@Override
public void onCreate(Bundle _state){
    doBindService()
}
private void doBindService(){
    Intent intent = new Intent(MyActivity.this, MusicService.class);
    bindService(intent, connc,Context.BIND_AUTO_CREATE);
}
private ServiceConnection connc=new ServiceConnection() {            
    @Override
    public void onServiceDisconnected(ComponentName name) {    
    }
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {        
        mService=((MusicService.LocalBinder)service).getService();        
    }
};

要使用service上的function就可以直接用,如 mService.start(),mService.stop()...等定義在Service內public的方法。若要取得回傳值,只要service內的function有回傳值,直接接收就可以了,例如
String _path=mService.getSongPath();

3.Service

內定義一個內部Binder class可以用來取得service

public class LocalBinder extends Binder {
     MusicService getService() {
        return  MusicService.this;
    }
}
private final IBinder mBinder = new LocalBinder();
@Override
public IBinder onBind(Intent intent) {    
    return mBinder;
}


認識Android環境裡的兩種Service

 在Android平台裡,上層應用程式的service與底層的系統service有很大的區別,只是許多人都將它們混在一起了。例如,在Android裡著名的MediaPlayer範例,許多人都知道其結構為:

 
     1

其中有兩個service
l   應用程式開發者所寫的myPlayerService.java類別。這是屬於一般所稱的SDK service
l   Android已經提供的MediaPlayerService.cpp類別。這是屬於一般所稱的系統service

     主要的系統service都是在Android系統啟動時,就會先逐一登記到BD(Binder Driver)裡,隨時準備為SDK應用程式進行服務。
    天字第一號的系統service就是ServiceManager。當Android系統啟動時,就會優先將之登記到BD(Binder Driver)裡,如下圖:


 
        2

      讓其它組件能透過IBinder介面(可轉型為IServiceManager介面)而遠距呼叫ServiceManager的服務。於是陸續會有更多的系統service呼叫ServiceManageraddService()函數而登記到BD裡成為可遠距呼叫的系統service。例如,基於上述途徑,ActivityManagerService就會登記為可遠距呼叫的系統service。再如,MediaPlayerService也繼續登記為基本的系統service,於是ActivityManagerService和MediaPlayerService也都具有IBinder介面了,如下圖:

   
        3

Android系統啟動完成之後,就可以執行應用程式了。
     執行應用程式時,就由ActivityManagerService來將SDK service(myPlayerService.java類別)登記到Binder Driver裡,於是應用程式裡的Activity等類別就能使用bindService()函數來繫結(bind)myPlayerService,然後透過IBinder介面而遠程呼叫到myPlayerService.java之後,myPlayerService再透過圖1的結構而呼叫到MediaPlayerService,進而呼叫到OpenCode組件,就播放出好聽的MP3音樂了。

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



2.Service生命周期
Service的生命周期并不像Activity那么复杂,它只继承了onCreate(),onStart(),onDestroy()三个方法,当第一次启动Service时,先后调用了onCreate(),onStart()这两个方法,当停止Service时,则执行onDestroy()方法,这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法。
 
二.Service的启动方式
Service的有两种启动方式:Context.startService()和Context.bindService(),这两种方式对Service生命周期的影响是不同的。
 
1.Context.startService()方式启动
 
①Context.startService()方式的生命周期: 启动时,startService –> onCreate() –> onStart()
停止时,stopService –> onDestroy()
如果调用者直接退出而没有停止Service,则Service 会一直在后台运行
 
Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
 
2.Context.bindService()方式启动:
 
Context.bindService()方式的生命周期: 绑定时,bindService -> onCreate() –> onBind()
调用者退出了,即解绑定时,Srevice就会unbindService –>onUnbind() –> onDestory()
 
用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法。
 
②Context.bindService()方式启动 Service的方法:
绑定Service需要三个参数:bindService(intent, conn, Service.BIND_AUTO_CREATE);
第一个:Intent对象
第二个:ServiceConnection对象,创建该对象要实现它的onServiceConnected()和 onServiceDisconnected()来判断连接成功或者是断开连接
第三个:如何创建Service,一般指定绑定的时候自动创建
 
三.startService 和 bindService的区别
 
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。
 
使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
 
如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务但会导致多次调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
 
如果打算采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法。




沒有留言:

張貼留言