Android Service介紹


本文主要介紹Service的概念及作用、使用(包括長時間運行的服務、應用內交互的服務、應用間交互的服務的分別舉例、生命周期、使用場景)、特殊的Service

示例代碼見ServiceDemo示例APK見:TrineaAndroidDemo.apk

 
1、概念及作用

由於ANR對Activity和BroadcastReceiver響應時間的限制(Activity對事件響應不超過5秒,BroadcastReceiver執行不超過10秒),使得在其中都不適合執行較耗時操作,這樣像網絡、數據庫、復雜計算這類耗時操作的執行就需要一個組件來承擔。Service作為Android四大組件之一,其功能之一就是耗時操作的執行,主要功能如下:

a. 執行需要長時間運行的操作,這個操作不與用戶進行交互,如網絡下載、大文件I/O、復雜計算。
b. 應用內或應用間數據通信,Android每個應用程序都在自己的dalvik虛擬機中運行,一個應用是不允許訪問其他應用的內存信息的,為此Android引入了Content Provider在不同應用間共享數據,BroadcastReceiver廣播信息給不同應用程序,但Content Provider更多用於數據的共享,BroadcastReceiver廣播的信息會被所有應用接收較耗費系統資源,對於兩個應用間動態的進行交互還需要通過Service來完成。

 

2、使用
(1) startService啟動不可進行交互的Service

a. 示例代碼及介紹

Service示例
public class MyService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(this, "Service Create", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDestroy() {
        Toast.makeText(this, "Service Destroty", Toast.LENGTH_SHORT).show();
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(this, "Service Start", Toast.LENGTH_SHORT).show();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

服務都必須在AndroidManifest.xml文件中注冊<service android:name=".MyService"/>,在Activity中定義對象private Intent myServiceIntent = new Intent(this, MyService.class);在onCreate函數中startService(myServiceIntent)啟動服務; onDestroy函數中stopService(myServiceIntent)關閉服務;

 

b. 生命周期
通過startService啟動服務,若服務未啟動,會先執行onCreate函數(若服務已啟動則不執行此函數),再執行onStartCommand函數。由此可知多次調用startService傳入相同參數不會啟動多個服務(onStartCommand函數會執行多次),所以最終只需要調用一次stopService或stopSelf函數停止服務;我們可以將service的處理邏輯放入onStartCommand函數中。服務一直運行,在程序退出后服務也不會停止,直到stopService或stopSelf函數被調用,當然可能被系統回收。

對於onStartCommand的返回值,若返回START_STICKY表示服務通過顯式調用啟動或停止,若返回START_NOT_STICKY orSTART_REDELIVER_INTENT表示服務僅在有請求發送過來處理時才處於運行狀態。

 

c. 使用場景

因為這種方式Service無法與外部進行方便的動態交互,所以適合做后台服務,如網絡下載(用戶通過Intent傳入Url到Service,推薦使用IntentService).

 

(2) bindService啟動的Service應用內交互

a. 示例代碼及介紹

在上面的方式中Context可以通過Intent向Service傳入簡單的信息,但是如果希望調用Service的接口進行操作或是獲取Service的屬性則無法實現,這里我們可以通過bindService實現,如下先定義自己的服務

自定義Service
public class MyService extends Service {

    private int      count;
    private MyBinder myBinder = new MyBinder();

    @Override
    public void onCreate() {
        Toast.makeText(this, "Service onCreate", Toast.LENGTH_SHORT).show();
        count = 0;
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        Toast.makeText(this, "Service onDestroy", Toast.LENGTH_SHORT).show();
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 服務被綁定時調用
     * 返回值用於讓調用者和服務通信,傳入ServiceConnection的public void onServiceConnected(ComponentName name, IBinder service)函數第二個參數
     */
    @Override
    public IBinder onBind(Intent intent) {
        return myBinder;
    }

    public int getCount() {
        return count;
    }

    public int increaseCount() {
        return ++count;
    }

    public int decreaseCount() {
        return --count;
    }

    public class MyBinder extends Binder {

        MyService getService() {
            return MyService.this;
        }
    }
}

從上可以看出我們重寫onBind函數並返回自己的Binder用於調用者和服務之間通信。下面我們在調用者中進行調用

定義自己的Service、Intent、ServiceConnection對象
private MyService         myService;
private Intent            myServiceIntent;
private ServiceConnection con = new ServiceConnection() {

                                  /**
                                   * 服務所在進程被kill或是crash時系統調用,而不是unbindService時調用
                                   */
                                  @Override
                                  public void onServiceDisconnected(ComponentName name) {
                                      Toast.makeText(getApplicationContext(), "Service disconnect",
                                                     Toast.LENGTH_SHORT).show();
                                  }

                                  /**
                                   * 服務連接時調用,若已經連接不進行調用
                                   */
                                  @Override
                                  public void onServiceConnected(ComponentName name, IBinder service) {
                                      myService = ((MyBinder)service).getService();
                                      Toast.makeText(getApplicationContext(), "Service Connect", Toast.LENGTH_SHORT).show();
                                  }
                              };

接着調用bindService(myServiceIntent, con, Context.BIND_AUTO_CREATE);綁定服務,綁定成功返回true。這時會執行ServiceConnection對象的onServiceConnected函數為myService變量賦值。接着我們就可以通過myService.increaseCount();操作Service的屬性,myService.getCount()獲得Service的屬性,這樣我們就成功和Service進行了通信。在不需要通信時通過unbindService(con);解除服務綁定。

 

b. 生命周期
通過bindService綁定服務,若服務未啟動,會先執行Service的onCreate函數,再執行onBind函數,最后執行ServiceConnection對象的onServiceConnected函數。若服務已啟動但尚未綁定,先執行onBind函數,再執行ServiceConnection對象的onServiceConnected函數。若服務已綁定成功,則直接返回。這里不會自動調用onStartCommand函數。

通過unbindService解除綁定服務,若已綁定成功,會先執行Service的onUnbind函數,再執行onDestroy函數,注意這里不會執行ServiceConnection對象的onServiceDisconnected函數,因為該函數是在服務所在進程被kill或是crash時被調用。若服務尚未綁定系統會報服務尚未注冊異常,我們可以通過如下代碼解決

if (myService != null) {
    unbindService(con);
    myService = null;
}

與startService啟動的Service不同,若服務通過bindService啟動並且沒有通過startService啟動過后,則在連接斷開時服務就會自動解綁(onUnbind)並終止(onDestroy),而在調用者(Activity)退出后會自動斷開連接,所以這時服務會自己解綁並終止。若存在某個組件綁定了該服務,則調用該服務的stopService不會停止服務

 

c. 使用場景

應用內通信,如音樂播放器,在服務中控制播放器的播放、暫停、停止,在Activity中通過對服務操作控制播放器。


(3) bindService啟動的Service應用間交互——AIDL

在Android AIDL應用間交互中 詳細介紹見Android AIDL交互

 

通過上面的三個例子我們可以看出Context.startService()Context.bindService()的區別

a. bindService啟動的Service可以和Context進行交互,而startService啟動的Service不可以交互,因而使用場景也不同

b. 執行的生命周期不同

 

3、特殊的Service

(1). 異步服務IntentService

默認Service是運行在主線程內的 ,如果在Service內運行一個耗時操作就會阻塞主線程,可能導致ANR,為此我們可以在Service中自己新建線程去執行耗時操作,不過Android系統引入了IntentService方便的解決了這個問題, IntentService會啟動一個工作線程去完成用戶onHandleIntent中定義的操作,需要注意的是對於同一個IntentService的多次請求(startService調用),在同一個線程中處理,一次只會執行一個請求的onHandleIntent函數。對於不同IntentService的同時請求,在不同的線程中處理,所以每個請求的onHandleIntent函數可以並發執行。示例代碼如下:

IntentService示例
public class MyIntentService extends IntentService {

    public MyIntentService(){
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            System.out.println("IntentService1 Begin Sleep. " + "Thread name: " + Thread.currentThread().getName()
                               + ", Thread Id: " + Thread.currentThread().getId());
            Thread.sleep(3000);
            System.out.println("IntentService1 End. ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在AndroidManifest.xml文件中注冊服務<service android:name=".MyIntentService"/>,在Activity中定義service對象private Intent myIntentServiceIntent = new Intent(ServiceDemo.this, MyIntentService.class);在onCreate函數中startService(myIntentServiceIntent)啟動服務;

從上面我們可以看出
a. IntentService只需要重定義onHandleIntent函數並定義一個無參構造函數(xml中服務注冊初始化時使用)即可。
b. IntentService服務在onHandleIntent執行結束后會自動關閉。

 

IntentService和普通Service的區別如下
a. 普通Service運行在主線程中,IntentService運行在一個工作線程中不會阻塞主線程。
b. 普通Service需要手動調用停止接口,IntentService自動停止。
c. IntentService的onStartCommand函數根據mRedelivery屬性值返回START_REDELIVER_INTENT或START_NOT_STICKY,而普通Service自定義返回。

 

參考:
http://developer.android.com/reference/android/app/Service.html

http://developer.android.com/guide/components/services.html

http://developer.android.com/reference/android/app/IntentService.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM