Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Service(服務)

Service(服務)

編輯:關於android開發

Service(服務)


(一)、Service 簡介:

1、何謂“Service”?

“Service”意思即“服務”的意思,像 Windows 上面的服務一樣,服務是在後台上運行,承擔著靜悄悄的不為人所注意的工作。Service運行在後台,它是不可見的、無界面的程序。Service可以在很多場合的應用中使用,比如播放多媒體的時候用戶啟動了其他Activity,這個時候程序要在後台繼續播放;比如檢測SD卡上文件的變化;再或者在後台記錄用戶的地理信息位置的改變;或者啟動一個服務來運行並一直監聽某種動作等等。

2、Service概念的總結:

  • Service在後台運行,不可以與用戶直接交互;
  • 一個服務不是一個單獨的進程。服務對象本身並不意味著它是在自己的進程中運行,除非另有規定,否則它與運行程序是同在一個進程中;
  • 一個服務不是一個單獨的線程。Service和其他組件一樣,默認情況下,Service中的所有代碼都是運行在主線程中;
  • Service存在的價值雖然不如Activity那麼清晰。但是一般都讓Service執行耗時較長的操作。例如:播放音樂、下載文件、上傳文件等等。但是因為Service默認運行在主線程中,因此不能直接用它來做耗時的請求或者動作,最好在Service中啟動新線程來運行耗時的任務;
  • 需要通過某一個Activity或其他Context對象來啟動Service。context.startService() 或context.bindService();
  • Service很大程度上充當了應用程序後台線程管理器的角色。(如果Activity中新開啟一個線程,當該Acitivyt關閉後,該線程依然在工作,但是與開啟它的Activity失去聯系。也就是說此時的這個線程處於失去管理的狀態。但是使用Service,則可以對後台運行的線程有效地管理。)

    3、為什麼不使用後台線程而使用Service?

    • 1、Service可以放在獨立的進程中,所以更安全;
    • 2、使用Service可以依賴現有的binder機制,不需要在應用層面上處理線程同步的繁雜工作;
    • 3、系統可以重新啟動異常死去的Service。

      4、Service 與 Activity 的相同點與不同點:

      • 不同點:Activity是與用戶交互的組件,即可以看到UI界面,而Service是在後台運行、無需界面;
      • 相同點:使用Activity 時我們需要在配置文件中聲明標簽,同樣的使用Service 也需要在配置文件中聲明標簽。都具有一定的生命周期。

        (二)、服務的分類:

        1、本地服務Local Service:

        Local Service 用於應用程序內部。用於實現應用程序自己的一些耗時任務,比如查詢升級信息,並不占用應用程序比如Activity所屬線程,而是單開線程後台執行,這樣用戶體驗比較好。

        啟動service有兩種方法:

        1)、 Context.startService()

        調用者與服務之間沒有關聯,即使調用者退出,服務仍可運行

        2)、 Context.bindService()

        調用者與服務綁定在一起,調用者一旦退出,服務也就終止

        A、根據啟動方式將本地服務分為:啟動服務Started Service和綁定服務【重點】
        • Started Service:被啟動的服務

          被啟動的服務是由其它組件調用startService()方法而啟動的,該方法會導致被啟動服務的生命周期方法onStartCommand()被回調。當服務是被啟動狀態後,其生命周期與啟動它的組件無關,即使啟動服務的組件(Activity,BroadcastReceiver)已經被銷毀,該服務還可以在後台無限期運行。除非調用stopSelf()或stopService()來停止該服務。

          • Bind Service:被綁定的服務

            綁定服務是允許其它應用程序綁定並且與之交互的Service的實現類。為了提供綁定,必須實現onBind()回調方法。該方法返回IBinder對象,它定義了服務類與Activity交互的程序接口。

            Activity通過bindService()方法綁定到服務類,同時Activity必須提供ServiceConnection接口的實現類,它監視Activity與服務類之間的連接。在重寫ServiceConnection接口的onServiceConnected()方法時,實現了將服務類順利賦值到了Activity中,實現了在Activity中使用該服務類並執行其中的方法。

            B、根據onStartCommand()回調方法的返回值,將Service分為粘性Service和非粘性Service:

            onStartCommand()方法有三種返回值:

            • START_STICKY(常量值:1):sticky的意思是“粘性的”。使用這個返回值時,我們啟動的服務跟應用程序"粘"在一起,如果在執行完onStartCommand後,服務被異常kill掉,系統會自動重啟該服務。當再次啟動服務時,傳入的第一個參數將為null;
            • START_NOT_STICKY(常量值:2):“非粘性的”。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統不會自動重啟該服務。
            • START_REDELIVER_INTENT(常量值:3):重傳Intent。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統會自動重啟該服務,並將Intent的值傳入。

              【備注:】

              以上三種情況,可以理解為發生車禍後的人:

              • START_STICKY:(常量值:1)車禍後自己蘇醒,但是失憶;
              • START_NOT_STICKY:(常量值:2)車禍後再也沒有蘇醒;
              • START_REDELIVER_INTENT:(常量值:3)車禍後自己蘇醒,依然保持記憶。

                2、遠程服務Remote Service:(明天講)

                Remote Service 用於android系統內部的應用程序之間。可以定義接口並把接口暴露出來,以便其他應用進行操作。可被其他應用程序復用,比如天氣預報服務,其他應用程序不需要再寫這樣的服務,調用已有的即可。

                (三)、Service的生命周期:

                1、Started Service的生命周期:

                • onCreate():創建服務
                • onStartCommand():服務開始運行(在2.0以前版本中,使用onStart()回調方法)
                • onDestroy() :服務被停止

                  【詳細說明:】

                  • 在程序中調用:context.startService() 會觸發執行Service生命周期中的onCreate()、onStartCommand()回調方法,此時服務就開始正式運行;
                  • 如果Service還沒有運行,則android先調用onCreate()然後調用onStartCommand();如果Service已經運行,則只調用onStartCommand(),所以一個Service的onStartCommand方法可能會重復調用多次;
                  • 如果在程序中調用:context.stopService()會觸發執行Service生命周期中的onDestroy()回調方法,會讓服務停止;
                  • stopService()的時候直接onDestroy,如果是調用者自己直接退出而沒有調用stopService()的話,Service會一直在後台運行。該Service的調用者再啟動該Service後可以通過stopService關閉Service;或者在Service中調用stopSelf()方法自行銷毀。
                  • 所以StartService的生命周期為:onCreate -->onStartCommand(可多次調用) --> onDestroy。

                    2、Bind Service的生命周期:

                    • onCreate():創建服務
                    • onBind():綁定服務,服務開始運行
                    • onUnbind():取消綁定
                    • onDestroy() :服務被停止

                      【詳細說明:】

                      • 在程序中調用:context.bindService()會觸發執行Service生命周期中的onCreate()、onBind()回調方法,此時服務開始運行;
                      • onBind將返回給客戶端一個IBind接口實例,IBind允許客戶端回調服務的方法,比如得到Service運行的狀態或其他操作。此後調用者(Context,例如Activity)會和Service綁定在一起;
                      • 如果調用Service的調用者Context退出了,那麼會依次調用Service生命周期中的onUnbind()、onDestroy()回調方法,會讓服務停止;
                      • 所以BindService的生命周期為:onCreate --> onBind(只一次,不可多次綁定) --> onUnbind --> onDestory。

                        【備注:】

                        • Service是不能自己啟動的,只有通過 Context 對象調用startService()和bindService()方法來啟動。
                        • 在Service每一次的開啟關閉過程中,只有onStartCommand()可被多次調用(通過多次startService調用),其他onCreate()、onBind()、onUnbind()、onDestory()在一個生命周期中只能被調用一次。
                        • Service可以在和多場合的應用中使用,比如播放多媒體的時候用戶啟動了其他Activity這個時候程序要在後台繼續播放,比如檢測SD卡上文件的變化,再或者在後台記錄你地理信息位置的改變等等,總之服務總是藏在後頭的。

                          二、本地Service:

                          (一)、Started Service啟動MediaPlayer播放音樂:

                          1、操作步驟:

                          • 1、寫xml布局文件;
                          • 2、寫MainActivity文件,通過按鈕點擊事件啟動Service;
                          • 3、寫繼承於Service的MusicService類文件:重寫onCreate()/onStartCommand()/onDestroy()回調方法。

                            2、MusicService核心代碼:

                            public class MusicService extends Service {

                            private MediaPlayer mediaPlayer;

                            private boolean isStop = true;

                            @Nullable

                            @Override

                            public IBinderonBind(Intent intent) {

                            return null;

                            }

                            @Override

                            public void onCreate() {

                            super.onCreate();

                            if (mediaPlayer == null) {

                            mediaPlayer = new MediaPlayer();

                            //當音樂播放完成時調用

                            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {

                            @Override

                            public void onCompletion(MediaPlayer mp) {

                            //播放完成之後發送廣播給Activity

                            Intent intent = new Intent("play_completion");

                            sendBroadcast(intent);

                            }

                            });

                            }

                            // AlertDialog.Builder b = newAlertDialog.Builder(this);

                            }

                            @Override

                            public int onStartCommand(Intent intent, intflags, int startId){

                            //獲得Activity傳遞過來的state

                            int state= intent.getIntExtra("state", 0);

                            switch (state){

                            //開始播放

                            case 0:

                            if (isStop) {

                            preparePlayMusic();

                            isStop = false;

                            } else if (mediaPlayer != null &&!mediaPlayer.isPlaying()) {

                            mediaPlayer.start();

                            }

                            break;

                            //暫停播放

                            case 1:

                            if (mediaPlayer != null && mediaPlayer.isPlaying()) {

                            mediaPlayer.pause();

                            }

                            break;

                            //停止播放

                            case 2:

                            if (mediaPlayer != null) {

                            mediaPlayer.stop();

                            isStop = true;

                            }

                            break;

                            }

                            //START_STICKY,被系統回收之後,服務重啟,同時intent為null

                            //START_NOT_STICKY,被系統回收之後,服務不會重啟

                            //START_REDELIVER_INTENT,被系統回收之後,服務重啟,同時intent還為之前的intent

                            return this.START_NOT_STICKY;

                            }

                            private void preparePlayMusic() {

                            try {

                            //重置MediaPlayer

                            mediaPlayer.reset();

                            //設置播放數據源

                            mediaPlayer.setDataSource(Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator +

                            "xiami" + File.separator + "audios" + File.separator + "被動.mp3"

                            );

                            //准備播放

                            mediaPlayer.prepare();

                            //開始播放

                            mediaPlayer.start();

                            //設置是否循環播放

                            mediaPlayer.setLooping(false);

                            } catch (IOExceptione) {

                            e.printStackTrace();

                            }

                            }

                            @Override

                            public void onDestroy() {

                            super.onDestroy();

                            }

                            }

                            3、Activity核心代碼:

                            public class MainActivity extends AppCompatActivity {

                            private Intent intent;

                            private MyReceiver myReceiver;

                            private IntentFilter intentFilter;

                            @Override

                            protected void onCreate(Bundle savedInstanceState) {

                            super.onCreate(savedInstanceState);

                            setContentView(R.layout.activity_main);

                            intent = new Intent(this, MusicService.class);

                            myReceiver = new MyReceiver();

                            intentFilter = new IntentFilter();

                            intentFilter.addAction("play_completion");

                            }

                            public void start(View view) {

                            intent.putExtra("state", 0);

                            startService(intent);

                            }

                            public void pause(View view) {

                            intent.putExtra("state", 1);

                            startService(intent);

                            }

                            public void stop(View view) {

                            intent.putExtra("state", 2);

                            startService(intent);

                            }

                            @Override

                            protected void onStart() {

                            super.onStart();

                            registerReceiver(myReceiver,intentFilter);

                            }

                            @Override

                            protected void onStop() {

                            super.onStop();

                            unregisterReceiver(myReceiver);

                            }

                            }

                            4、Started Service總結:

                            • Activity頁面中需要startService(intent) 和stopService(intent)兩個方法來啟動Service和停止Service;
                            • 繼承於Service類的自定義子類——MusicService類中,生命周期回調方法有:onCreate() 、onStartCommand() 、onDestroy();
                            • 如果停止服務,可以在Activity中調用stopService(intent),也可以在Service中執行stopSelf()方法;
                            • 執行停止服務方法,會回調Service生命周期中的onDestroy()方法;
                            • 如果希望關閉Activity窗體,服務也停止,那麼在Activity的onDestroy()方法中執行stopService()方法。如果希望關閉窗體後,服務還繼續,那麼Activity的onDestroy()中不執行停止服務即可;
                            • 在StartService中不會回調onBind()方法;
                            • 在停止服務後,如果再次點擊“播放”,可以重新啟動StartService。

                              5、IntentService與Service:

                              不管是何種Service,它默認都是在應用程序的主線程(亦即UI線程)中運行的。所以,如果你的Service將要運行非常耗時或者可能被阻塞的操作時,你的應用程序將會被掛起,甚至會出現ANR錯誤。為了避免這一問題,你應該在Service中重新啟動一個新的線程來進行這些操作。現有兩種方法大家參考:

                              ① 直接在Service的onStartCommand()方法中新建一個線程來執行;

                              ② Android SDK 中為我們提供了一個現成的Service類來實現這個功能,它就是IntentService,它主要負責以下幾個方面:

                              • 生成一個默認的且與主線程互相獨立的工作者線程來執行所有傳送至 onStartCommand() 方法的Intetnt
                              • 生成一個工作隊列來傳送Intent對象給你的onHandleIntent()方法,同一時刻只傳送一個Intent對象,這樣一來,你就不必擔心多線程的問題。
                              • 在所有的請求(Intent)都被執行完以後會自動停止服務,所以,你不需要自己去調用stopSelf()方法來停止該服務
                              • 提供了一個onStartCommand()方法的默認實現,它將Intent先傳送至工作隊列,然後從工作隊列中每次取出一個傳送至onHandleIntent()方法,在該方法中對Intent做出相應的處理

                                IntentService使用隊列的方式將請求的Intent加入隊列,然後開啟一個worker thread(線程)來處理隊列中的Intent,對於異步的startService請求,IntentService會處理完成一個之後再處理第二個,每一個請求都會在一個單獨的workerthread中處理,不會阻塞應用程序的主線程。

                                這裡就給我們提供了一個思路,如果有耗時的操作可以在Service裡面開啟新線程,也可以使用IntentService來處理耗時操作。但你若是想在Service中讓多個線程並發的話,就得使用第一種方法,在Service內部起多個線程。

                                (二)、Bind Service啟動MediaPlayer播放音樂:

                                1、操作步驟:

                                • 1、寫xml布局文件;
                                • 2、寫MainActivity文件,構建ServiceConnection對象,重寫其中的抽象方法onServiceDisconnected()和onServiceConnected();
                                • 3、寫繼承於Service的BindService類文件,定義繼承於Binder的內部類MyBinder,在其中定義方法getService();
                                • 4、BindService類中重寫onCreate()方法、重寫onBind()回調方法,onBind()方法返回MyBinder對象,重寫onDestroy()方法;

                                  2.MusicService核心代碼:

                                  public class MyService extends Service {

                                  private MediaPlayer mediaPlayer;

                                  private boolean isStop = true;

                                  private File[] files;

                                  private int currentPosition = 0;

                                  //創建服務時調用

                                  @Override

                                  public void onCreate() {

                                  super.onCreate();

                                  if (mediaPlayer == null) {

                                  mediaPlayer = new MediaPlayer();

                                  // preparePlayMusicx();

                                  mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {

                                  @Override

                                  public void onCompletion(MediaPlayer mp) {

                                  currentPosition++;

                                  preparePlayMusicx();

                                  isStop = false;

                                  }

                                  });

                                  }

                                  Log.d("lenve", "onCreate:");

                                  }

                                  private void preparePlayMusicx() {

                                  try {

                                  mediaPlayer.reset();

                                  mediaPlayer.setDataSource(files[currentPosition].getAbsolutePath());

                                  mediaPlayer.prepare();

                                  mediaPlayer.start();

                                  } catch (IOExceptione) {

                                  e.printStackTrace();

                                  }

                                  }

                                  //創建時調用

                                  @Nullable

                                  @Override

                                  public IBinderonBind(Intent intent) {

                                  files =((MusicFiles) intent.getSerializableExtra("files")).getFiles();

                                  Log.d("lenve", "onBind:");

                                  return new MyBinder();

                                  }

                                  //解除綁定時調用

                                  @Override

                                  public boolean onUnbind(Intent intent) {

                                  Log.d("lenve", "onUnbind:");

                                  return super.onUnbind(intent);

                                  }

                                  //解除綁定時調用

                                  @Override

                                  public void onDestroy() {

                                  super.onDestroy();

                                  Log.d("lenve", "onDestroy:");

                                  }

                                  //自定義一個類繼承自Binder

                                  class MyBinderextends Binder {

                                  //在該類中定義一個方法,該方法返回當前Service的實例

                                  public MyServicegetService() {

                                  //返回當前Service的實例

                                  return MyService.this;

                                  }

                                  }

                                  public void play() {

                                  if (isStop) {

                                  preparePlayMusicx();

                                  isStop = false;

                                  } else if (mediaPlayer != null &&!mediaPlayer.isPlaying()) {

                                  mediaPlayer.start();

                                  }

                                  }

                                  public void pause() {

                                  if (mediaPlayer != null && mediaPlayer.isPlaying()) {

                                  mediaPlayer.pause();

                                  }

                                  }

                                  public void stop() {

                                  if (mediaPlayer != null) {

                                  mediaPlayer.stop();

                                  isStop = true;

                                  }

                                  }

                                  }

                                  3.Activity核心代碼:

                                  public class MainActivity extends AppCompatActivity {

                                  private Intent intent;

                                  private ServiceConnection conn;

                                  private MyService myService;

                                  @Override

                                  protected void onCreate(Bundle savedInstanceState) {

                                  super.onCreate(savedInstanceState);

                                  setContentView(R.layout.activity_main);

                                  //獲取音樂文件列表

                                  File[] listFiles = new File(Environment.getExternalStorageDirectory().getAbsolutePath()

                                  + File.separator + "xiami"+ File.separator + "audios").listFiles(new FileFilter(){

                                  //過濾出所有的mp3文件

                                  @Override

                                  public boolean accept(File pathname) {

                                  if (pathname.getName().endsWith(".mp3")) {

                                  return true;

                                  }

                                  return false;

                                  }

                                  });

                                  intent = new Intent(this, MyService.class);

                                  intent.putExtra("files", new MusicFiles(listFiles));

                                  //當Activity和Service產生連接之後觸發該方法

                                  //Activity和Service連接斷開後調用該方法

                                  conn = new ServiceConnection() {

                                  //當Activity和Service產生連接之後觸發該方法

                                  @Override

                                  public void onServiceConnected(ComponentName name, IBinder service) {

                                  myService = ((MyService.MyBinder) service).getService();

                                  Log.d("lenve", "onServiceConnected:");

                                  }

                                  //Activity和Service連接斷開後調用該方法

                                  @Override

                                  public void onServiceDisconnected(ComponentName name) {

                                  Log.d("lenve", "onServiceDisconnected:");

                                  }

                                  };

                                  //綁定服務,返回值表示綁定是否成功

                                  boolean flag = bindService(intent, conn,Service.BIND_AUTO_CREATE);

                                  }

                                  public void bindService(View view) {

                                  boolean flag = bindService(intent, conn,Service.BIND_AUTO_CREATE);

                                  }

                                  public void unbindService(View view) {

                                  unbindService(conn);

                                  }

                                  public void play(View view) {

                                  myService.play();

                                  }

                                  public void pause(View view) {

                                  myService.pause();

                                  }

                                  public void stop(View view) {

                                  myService.stop();

                                  }

                                  }

                                  三、 拓展知識(進程和生命周期):

                                  Android操作系統嘗試盡可能長時間保持應用的進程,但當可用內存很低時要移走一部分進程。哪些程序可以運行,哪些要被銷毀?答案是:重要級別低的進程可能被淘汰。

                                  按照重要性排列,一共可以分成5級:

                                  1、前台運行進程:

                                  用戶此時需要處理和顯示的進程。符合下列條件任何一個,這個進程就被認為是前台運行進程。

                                  • 與用戶正發生交互;
                                  • 它控制一個與用戶交互的必須的基本的服務;
                                  • 一個正在調用生命周期回調方法的ervice(如onCreate()、onStar()、onDestroy());
                                  • 一個正在運行onReceive()方法的廣播接收對象。

                                    銷毀前台運行進程是系統萬不得已的、最後的選擇——當內存不夠系統繼續運行下去時,殺掉一些前台進程來保證能夠響應用戶的需求。

                                    2、可用進程:

                                    一個可用進程沒有任何前台組件,但它仍然可以影響到用戶的界面。下面情況發生時,可以稱該進程為可用進程。

                                    它是一個非前台的activity,但對用戶仍然可用(onPause()方法已經被調用)。例如:前台的activity是一個允許上一個activity可見的對話框。也就是說當前activity中是一個對話框,對話框之外的地方能看到前一個activity的界面。

                                    3、服務進程:

                                    服務進程是一個通過調用startService()方法啟動的服務,並且不屬於前兩種情況。盡管服務進程沒有直接被用戶看到,但他們確實是用戶所關心的,比如後台播放音樂或網絡下載數據,所以系統保證他們的運行。

                                    4、後台進程:

                                    一個後台進程就是非當前正在運行的activity(activity的onStop()方法已經被調用),他們不會對用戶體驗造成直接的影響,當沒有足夠內存來運行前台可見程序時,他們將會被終止。

                                    通常,後台進程會有很多個在運行,LRU最近使用程序列表來保證經常運行的activity能最後一個被終止。

                                    5、空線程:

                                    一個空線程沒有運行任何可用應用程序,保留他們的唯一原因是為了設立一個緩存機制,來加快組件啟動的時間。系統經常殺死這些內存來平衡系統的整個系統的資源,進程緩存和基本核心緩存之間的資源。、

                                    四、Service總結:

                                    1.Service是Android四大組件中與Activity最相似的組件,它們都代表可執行的程序

                                    2.Service與Activity的區別:

                                    >Service一直在後台運行,它沒有用戶界面,所以絕不會到前台來

                                    >它完全具有自己的生命周期

                                    3.應用場景:

                                    >音樂播放器、後台下載等

                                    4.Activity與Service的選擇標准

                                    >如果在運行時需要向用戶呈現界面,或該程序需要與用戶交互,就需要使用Activity

                                    >否則就應該考慮使用Service

                                    5.Service分類

                                    >本地服務(在應用內)

                                    >遠程服務(在另一個應用內)

                                    6.開發者開發Service的步驟與開發Activity的步驟很相似:

                                    A.創建一個類,繼承Service

                                    B.重寫Service類中的相關生命周期方法

                                    C.別忘記在清單文件中進行注冊

                                    7.Service與Activity的相似處:

                                    >都是從Context派生出來,因此都可以調用Context裡定義的如getResources()、getContentResolver()等方法

                                    8.服務的啟動方式:

                                    >服務不能自己運行,需要通過調用Context.startService()或Context.bindService()方法啟動服務

                                    >兩種啟動方式的區別:

                                    startService()啟動服務,啟動者與服務之間沒有關聯,即使訪問者退出了,服務仍然運行

                                    bindService()啟動服務,啟動者與服務之間綁定在了一起,啟動者退出,則服務也就終止--"不求同生,但求同死"

                                    9.服務的結束方式:

                                    >采用Context.startService()啟動的服務,只能調用Context.stopService()終止服務,

                                    服務結束會調用onDestroy()方法啟動服務

                                    >采用bindService()啟動的服務,隨著訪問者的退出而自動退出

                                    10.注意:

                                    >startService()啟動的服務,啟動者退出時,服務不會停止,除非調用stopService()

                                    >bindService()啟動服務,啟動者退出時,服務也應該隨之終止

                                    >service雖然在後台運行,也不要在其中執行耗時的任務(如網絡下載),請在相應的方法中開啟一個線程來處理

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved