轉自 http://blog.csdn.net/liuhe688/article/details/6874378html
富貴必從勤苦得,男兒須讀五車書。唐.杜甫《柏學士茅屋》java
做爲程序員的咱們,須知富貴是要經過勤苦努力才能獲得的,要想在行業內有所建樹,就必須刻苦學習和鑽研。android
今天咱們來說一下Android中Service的相關內容。程序員
Service在Android中和Activity是屬於同一級別上的組件,咱們能夠將他們認爲是兩個好哥們,Activity儀表不凡,迷倒萬千少女,常常作一些公衆人物角色,而Service一副彪悍的長相,但卻身強力壯,經常在後臺作一些搬運工的力氣活,雖然有些累,但你們都不能失去他。app
下面咱們就圍繞Service對其進行全面講解:異步
1.Service生命週期ide
Service生命週期能夠從兩種啓動Service的模式開始講起,分別是context.startService()和context.bindService()。學習
(1).startService的啓動模式下的生命週期:當咱們首次使用startService啓動一個服務時,系統會實例化一個Service實例,依次調用其onCreate和onStartCommand方法,而後進入運行狀態,此後,若是再使用startService啓動服務時,再也不建立新的服務對象,系統會自動找到剛纔建立的Service實例,調用其onStart方法;若是咱們想要停掉一個服務,可以使用stopService方法,此時onDestroy方法會被調用,須要注意的是,無論前面使用了多個次startService,只需一次stopService,便可停掉服務。this
(2).bindService啓動模式下的生命週期:在這種模式下,當調用者首次使用bindService綁定一個服務時,系統會實例化一個Service實例,並一次調用其onCreate方法和onBind方法,而後調用者就能夠和服務進行交互了,此後,若是再次使用bindService綁定服務,系統不會建立新的Service實例,也不會再調用onBind方法;若是咱們須要解除與這個服務的綁定,可以使用unbindService方法,此時onUnbind方法和onDestroy方法會被調用。spa
兩種模式有如下幾點不一樣之處:startService模式下調用者與服務無必然聯繫,即便調用者結束了本身的生命週期,只要沒有使用stopService方法中止這個服務,服務仍會運行;一般狀況下,bindService模式下服務是與調用者生死與共的,在綁定結束以後,一旦調用者被銷燬,服務也就當即終止,就像江湖上的一句話:不求同生,希望同死。
值得一提的是,之前咱們在使用startService啓動服務時都是習慣重寫onStart方法,在Android2.0時系統引進了onStartCommand方法取代onStart方法,爲了兼容之前的程序,在onStartCommand方法中其實調用了onStart方法,不過咱們最好是重寫onStartCommand方法。
以上兩種模式的流程以下圖所示:
![](http://static.javashuo.com/static/loading.gif)
下面咱們就結合實例來演示一下這兩種模式的生命週期過程。咱們新建一個名爲service的項目,而後建立一個MyService的服務類,代碼以下:
- package com.scott.service;
-
- import android.app.Service;
- import android.content.Intent;
- import android.os.IBinder;
- import android.util.Log;
-
- public class MyService extends Service {
-
- private static final String TAG = "MyService";
-
- @Override
- public void onCreate() {
- super.onCreate();
- Log.i(TAG, "onCreate called.");
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.i(TAG, "onStartCommand called.");
- return super.onStartCommand(intent, flags, startId);
- }
-
- @Override
- public void onStart(Intent intent, int startId) {
- super.onStart(intent, startId);
- Log.i(TAG, "onStart called.");
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- Log.i(TAG, "onBind called.");
- return null;
- }
-
- @Override
- public boolean onUnbind(Intent intent) {
- Log.i(TAG, "onUnbind called.");
- return super.onUnbind(intent);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.i(TAG, "onDestroy called.");
- }
- }
而後再AndroidManifest.xml中配置服務信息,否則這個服務就不會生效,配置以下:
- <service android:name=".MyService">
- <intent-filter>
- <action android:name="android.intent.action.MyService" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </service>
若是服務只是在本應用中使用,大能夠去掉<intent-filter>屬性。
服務搭建完成以後,咱們就來關注一下調用者MainActivity,它很簡單,只有兩個按鈕,一個是啓動服務,另外一個是中止服務,咱們來看一下他們的點擊事件:
- public void start(View view) {
- Intent intent = new Intent(this, MyService.class);
- startService(intent);
- }
-
- public void stop(View view) {
- Intent intent = new Intent(this, MyService.class);
- stopService(intent);
- }
接下來咱們就先點擊一次啓動按鈕,看看都發生了些什麼。日誌打印結果以下:
![](http://static.javashuo.com/static/loading.gif)
固然咱們以爲還不過癮,再點擊一次,咱們會發現結果略有不一樣:
![](http://static.javashuo.com/static/loading.gif)
咱們看到第二次點擊時onCreate方法就再也不被調用了,而是直接調用了onStartCommand方法(onStartCommand中又調用了onStart方法)。咱們選擇「Settings->Application s->Running services」就會發現咱們剛剛啓動的服務:
![](http://static.javashuo.com/static/loading.gif)
而後咱們點擊中止按鈕,試圖中止服務,咱們發現以下現象:
![](http://static.javashuo.com/static/loading.gif)
咱們會發現onDestroy方法被調用了,此時服務就中止運行了。咱們再次查看「Running services」,就會發現MyService這個服務已全無蹤影。
在這個過程當中,onBind方法和onUnbind方法始終沒被調用,咱們下面就讓這兩位show一下本身。
咱們修改一下MainActivity的代碼,使其能夠能夠以bindService的方式啓動一個服務,代碼以下:
- private ServiceConnection conn = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
-
- Log.i(TAG, "onServiceConnected called.");
- }
-
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- };
-
- public void bind(View view) {
- Intent intent = new Intent(this, MyService.class);
- bindService(intent, conn, Context.BIND_AUTO_CREATE);
- }
-
- public void unbind(View view) {
- unbindService(conn);
- }
在使用bindService綁定服務時,咱們須要一個ServiceConnection表明與服務的鏈接,它只有兩個方法,onServiceConnected和onServiceDisconnected,前者是在操做者在鏈接一個服務成功時被調用,然後者是在服務崩潰或被殺死致使的鏈接中斷時被調用,而若是咱們本身解除綁定時則不會被調用,因此咱們這裏只研究onServiceConnected這個方法。
看樣子是能夠去綁定一個服務了,其實還不行,由於咱們前面服務中的onBind方法返回值爲null,這樣是不行的,要想實現綁定操做,必須返回一個實現了IBinder接口類型的實例,該接口描述了與遠程對象進行交互的抽象協議,有了它咱們才能與服務進行交互。咱們因而有了這樣的代碼:
- @Override
- public IBinder onBind(Intent intent) {
- Log.i(TAG, "onBind called.");
- return new Binder() {};
- }
咱們返回了一個Binder的實例,而這個Binder偏偏是實現了IBinder接口,因此這樣就能夠實現綁定服務的操做了,一塊兒來演示一下。
先點擊一下綁定按鈕,咱們會發如今MainActivity中打印日誌以下:
![](http://static.javashuo.com/static/loading.gif)
似的,onServiceConnected方法被調用了,看來綁定鏈接已經成功了,看看MyService如何:
![](http://static.javashuo.com/static/loading.gif)
onCreate方法和onBind方法被調用了,此時服務已進入運行階段,若是再次點擊綁定按鈕,onCreate和onBinder並不會再次被調用,這個過程當中它們僅被調用一次。
而後點擊解除綁定按鈕,咱們會發現MyService打印以下:
![](http://static.javashuo.com/static/loading.gif)
能夠看到onUnbind方法和onDestroy方法被調用了,此時MyService已被銷燬,整個生命週期結束。
另外一方面,當咱們退出MainActivity時,服務也會隨之而結束,從這一點上看,MyService能夠說是誓死追隨着MainActivity。
須要注意的是,在鏈接中斷狀態再去作解除綁定操做會引發一個異常,在MainActivity銷燬以前沒有進行解除綁定也會致使後臺出現異常信息,此時咱們就要想辦法確保不會出現此類狀況,能夠這樣作:
- private boolean binded;
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- binded = true;
- }
-
- public void unbind(View view) {
- unbindService();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- unbindService();
- }
-
- private void unbindService() {
- if (binded) {
- unbindService(conn);
- binded = false;
- }
- }
以上就是bindService的生命週期,正如咱們上面講的同樣,使用bindService啓動服務後調用者和服務綁定到了一塊兒,當調用者被銷燬,服務也當即結終止。
一般狀況下是這樣的,不過也有特殊狀況。當startService和bindService在同一場合下使用時,就會出現稍微不一樣的現象。
若是咱們先以startService方式啓動服務,而後再用bindService綁定到這個服務,以後使用unbindService解除綁定,此時服務並不會所以而終止,而是繼續運行,直到咱們使用stopService來中止這個服務。下面咱們再修改一下代碼以驗證這個過程。MyService保持不變,咱們只需修改一下MainActivity。MainActivity最新代碼以下:
- package com.scott.service;
-
- import android.app.Activity;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.IBinder;
- import android.util.Log;
- import android.view.View;
-
- public class MainActivity extends Activity {
-
- private static final String TAG = "MainActivity";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- }
-
- private ServiceConnection conn = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.i(TAG, "onServiceConnected called.");
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- };
-
-
- public void start(View view) {
- Intent intent = new Intent(this, MyService.class);
- startService(intent);
- }
-
-
- public void bind(View view) {
- Intent intent = new Intent(this, MyService.class);
- bindService(intent, conn, Context.BIND_AUTO_CREATE);
- }
-
-
- public void unbind(View view) {
- unbindService(conn);
- }
-
-
- public void stop(View view) {
- Intent intent = new Intent(this, MyService.class);
- stopService(intent);
- }
- }
在MainActivity中包含了四個按鈕事件,分別是startService、bindService、unbindService和stopService,咱們逐一地按下,看看都發生了什麼。
首先按下啓動服務的按鈕,MyService打印以下:
![](http://static.javashuo.com/static/loading.gif)
恩,意料之中。而後咱們再按下綁定服務的按鈕,MyService打印以下:
![](http://static.javashuo.com/static/loading.gif)
此時,只有onBind被調用,以後二者就綁定成功。咱們再按下解除綁定的按鈕,MyService打印以下:
![](http://static.javashuo.com/static/loading.gif)
此時,onUnbind方法方法被調用,注意,此時MyService並無因解除綁定而終止,而是繼續運行。也許咱們內心會問,若是屢次按下綁定服務的按鈕或重複以上兩個步驟,結果如何呢?答案是onBind和onUnbind都不會再被調用了。看不到onBind被調用,是否是沒有綁定成功啊,咱們來看一下MainActivity打印信息:
![](http://static.javashuo.com/static/loading.gif)
重複按下綁定按鈕,幾回都綁定成功了。最後咱們按下中止服務的按鈕,MyService打印以下:
![](http://static.javashuo.com/static/loading.gif)
此時,onDestroy被調用了,此時MyService中止了運行,整個生命週期結束。
以上就是關於MyService生命週期的講解,下面咱們來介紹一下如何與服務進行通訊。與服務之間的通訊能夠分爲兩種,進程內的通訊和進程間的通訊,前者調用者和服務在同一應用進程內,然後者是分佈在不一樣應用進程中的。
2.進程內與服務通訊
進程內與服務通訊實際上就是經過bindService的方式與服務綁定,獲取到通訊中介Binder實例,而後經過調用這個實例的方法,完成對服務的各類操做。咱們上面也介紹了很多關於bindService的內容,下面咱們就針對實際需求對代碼作改動。首先是MyService,代碼以下:
- package com.scott.service;
-
- import android.app.Service;
- import android.content.Intent;
- import android.os.Binder;
- import android.os.IBinder;
- import android.util.Log;
-
- public class MyService extends Service {
-
- private static final String TAG = "MyService";
-
- @Override
- public IBinder onBind(Intent intent) {
- Log.i(TAG, "onBind called.");
- return new MyBinder();
- }
-
-
- public class MyBinder extends Binder {
-
-
- public void greet(String name) {
- Log.i(TAG, "hello, " + name);
- }
- }
- }
咱們建立了一個MyBinder的內部類,定義了一個greet方法,在onBind方法中就將這個MyBinder的實例返回,只要調用者獲取到這個實例,就能夠像拿着遊戲手柄同樣對服務進行操做。咱們來看一下調用者的代碼吧,MainActivity代碼以下:
- package com.scott.service;
-
- import android.app.Activity;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.IBinder;
- import android.view.View;
-
- public class MainActivity extends Activity {
-
-
- private MyService.MyBinder binder;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- }
-
- private ServiceConnection conn = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- binder = (MyService.MyBinder) service;
- binder.greet("scott");
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- };
-
-
- public void bind(View view) {
- Intent intent = new Intent(this, MyService.class);
- bindService(intent, conn, Context.BIND_AUTO_CREATE);
- }
-
-
- public void unbind(View view) {
- unbindService(conn);
- }
- }
在上面的代碼中,咱們是在綁定服務成功時將IBinder類型的service參數強轉爲MyService.MyBinder類型,獲取綁定中介實例,而後調用其greet方法。
操做一下,看看效果如何。先點擊綁定服務的按鈕,MyService打印以下:
![](http://static.javashuo.com/static/loading.gif)
須要注意的是,與服務綁定是一個異步的過程,也就是說,在這一刻咱們綁定服務,下一刻咱們去操做binder對象,也許它還爲null,這就容易引發空指針異常,正確的作法是把這些操做放到綁定成功以後,確保萬無一失。
以上就是進程內通訊的內容。