Android API Guides 閱讀筆記(8)----Service

Service是一個可以長時間在後臺執行並且沒有用戶界面的應用程序組件,Service同Activity一樣,也可以被設備上的其他應用程序中的組件調用。Service是運行在主線程(UI Thread)中的,所以,如果要做長時間的操作(比如下載,播放音樂等)應該在Service中新建線程或者使用IntentService,通過閱讀這節內容,將會瞭解到如下:

申明一個Service:

和創建Activity類似,需要在清單文件中(AndroidManifest.xml)申明。

  • 爲了保證Service只在自己的應用程序中運行,需要在< service >節點設置屬性 android:exported的值爲false

  • 爲了應用程序的安全,請一定要用顯示的Intent啓動或綁定Service

在其他組件中調用Service的兩種方式:

  • 通過startService()啓動一個Service:
    一旦啓動,便擁有其獨立的生命週期,並且可獨立的在後臺無限運行(即使調用它的組件被銷燬)
    使用這種方式創建的Service類會繼承Service.class或者IntentService.class。

    • Service.class:繼承這個類的Service,所有的操作默認都是在主線程(main/UI Thread)中進行,繼承它的子類Service在被啓動時會首先調用其中的onStartCommand()方法

    • IntentService:IntentService是Service的子類,通過繼承這個類的Service是在其他線程中執行操作,繼承它的子類Service在被啓動時會首先調用其中的onHandleIntent()方法

    如果調用時需要傳遞一些數據,則通過Intent,像在Activity中一樣添加數據,然後在onStartCommand()方法或者onHandleIntent()方法中獲取Intent

  • 通過bindService()綁定一個Service:
    只要有組件與其綁定,就開始運行,可以同時和多個組件綁定,直到所有綁定它的組件都解綁(unbind)了才銷燬,通過這種方式啓動的Service會調用其中的onBind()

其實,一個Service既可以通過啓動(start)運行,也可以通過綁定(bind)運行,這兩種方式並不是完全分離的,有時候會結合着使用,也就是說,一個啓動的Service,仍然可以被綁定,比如,通過startService()啓動一個音樂播放器的Service,如果後來用戶想控制這個音樂,比如獲取正在播放的音樂的信息,就可以通過bindService()實現

Service的生命週期:

Service的生命週期是獨立於調用它的組件的,也就是說一個組件一旦調用了這個Service,Service將自生自滅,不受那個組件的生命週期控制。其生命週期圖如下:
Service的生命週期

通過生命週期圖可以看到,創建一個Service時,根據調用的方式不同,其中執行的方法也不同。

  • 通過startService()啓動Service:通過Intent調用startService()可以啓動一個Service,系統會調用指定Service中的onStartCommand()方法(當然,如果這個Service之前沒有運行,將會首先調用Service中的onCreate()方法,然後再調用onStartCommand()方法)

  • 停止啓動的Service:通常Service是自己管理它的生命週期(而不是由系統管理)也就是說系統沒有辦法停止一個Service只有在Service中自己停止自己,這就需要在onStartCommand()中使用stopSelf()方法來停止一個Service,必要的時候也可以在其他組件中通過調用指定Service的stopService()方法來停止一個Service

  • 通過bindService()綁定Service:其他組件通過調用bindService()方法來綁定一個Service,爲了維持一個長久的連接,通常,綁定連接了,就不運行其他組件通過startService()方法來啓動這個已通過綁定連接的Service。客戶端(綁定這個Service的組件)和Service之間同連接通過IBinder維持,通過綁定,客戶端就可以調用Service中的方法,一個Service可以允許多個組件同時綁定它

  • 停止綁定Service:當所有的組件都與之解綁了,這個Service也就自動被系統銷燬

兩種Service類的區別(Service和IntentService):

  • 繼承IntentService.class:系統默認在其他線程中執行任務,而且只能執行單線程任務,如果傳入多個請求,需要通過隊列排隊執行,也就是雖然是在其他線程中執行任務,但是不能通過多線程執行,需要排隊執行。因爲大多數的Service不會同時執行多種操作(即,同時開多個線程,當然這樣做是危險的),所以官方推薦使用IntentService,使用它的好處是:

    • 繼承這個類的Service默認是在其他線程中執行

    • 將會創建一個工作隊列接收傳來的Intent,並通過onHandleIntent()方法來執行

    • 當所有請求都通過onHandleIntent()被執行完,Service就會自動停止(不需要調用stopSelf())默認的onBind(),並返回null

    • 提供的onStartCommand()接收Intent,然後發送給工作隊列,交給onHandleIntent()處理

    注意,在使用IntentService時通常只需要實現構造方法和onHandleIntent()方法,如果還想要實現其他的方法(如 onCreate(),onStartCommand(),onDestory()等)都需要返回父類的構造方法,也就是比如:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    return super.onStartCommand(intent,flags,startId);
}
  • 繼承Service.class:可以執行多線程任務(因爲你可以自定義創建Thread,如果需要同時處理多個請求,可以創建多個Thread來分別執行)

關於onStartCommand()方法:

onStartCommand()的返回值是一個integer類型的數據,表示:當系統銷燬當前Service後應該怎樣恢復(很重要的!!)

  • START_NOT_STICKY:當系統銷燬當前Service後,不會重新創建這個Service,除非有別的組件通過Intent重新啓動這個Service

  • START_STICKY:當系統銷燬當前Service後,將會重新創建這個Service,並調用其onStartCommand()方法,但是不會處理其中的Intent,因爲這個Intent是從別的組件啓動這個Service時用的,而重新啓動不是從別的組件啓動,所以不會處理這個Intent;

  • START_REDELIVER_INTENT:當系統銷燬當前Service後,會重新創建這個Service並調用其onStartCommand()方法,會處理上次傳入的Intent,所以的pending Intent(延遲Intent)將會被循環處理一遍(這裏不太理解。。。)

系統銷燬一個Service:

Android系統只有在內存資源嚴重的不足的時候纔會強行停止Service

  • 如果一個Service綁定在一個正在前臺運行的Activity,則不太可能被系統銷燬;

  • 如果一個Service申明爲“運行在前臺”,則也不太可能被銷燬。

  • 如果一個Service啓動並長時間在後臺運行,則有可能被系統銷燬

前臺Service(foreground service):

前臺運行的Service**基本不可能被系統銷燬,它會在狀態欄創建一個常駐的通知條**(想想360在手機狀態欄的那個),下面是一個啓動前臺Service的例子:

//創建通知
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
        System.currentTimeMillis());

Intent notificationIntent = new Intent(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);

//啓動一個前臺Service,需要一個通知作爲參數        
startForeground(ONGOING_NOTIFICATION_ID, notification);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章