Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android四大組件之Service的介紹

Android四大組件之Service的介紹

編輯:關於Android編程

Service的基本認識

Service是一個可以在後台執行長時間運行操作而不使用用戶界面的應用組件.Service可由其他應用組件啟動,而且即使用戶切換到其他應用,Service仍將在後台繼續運行.Service主要用於在後台處理一些耗時的邏輯,或者去執行某些需要長期運行的任務.必要的時候我們甚至可以在程序退出的情況下,讓Service在後台繼續保持運行狀態.

Service和Activity很相似,但是區別在於:Service一直在後台運行,沒有用戶界面,所以不會到前台,如果Service被啟動起來,就和Activity一樣,具有自己的聲明周期.另外,需要注意的是,Service和Thread不是一個意思,不要被Service的後台概念所迷惑.實際上Service並不會自動開啟線程,所有的代碼都是默認運行在主線程中的.因此,我們需要在Service的內部手動創建子線程,並在這裡執行具體的任務,否則可能造成ANR的問題.

Service的形式

Service基本上分為兩種形式:

Started(啟動的) 當應用組件(如 Activity)通過調用 startService() 啟動Service時,Service即處於“啟動”狀態.一旦啟動,Service即可在後台無限期運行,即使啟動Service的組件已被銷毀也不受影響.通常,一個開啟的Service執行單一操作並且不會給調用者返回結果.例如,它可能通過網絡下載或上傳文件.操作完成後,Service會自行停止運行.一個比較形象的比喻startService,//這種方式,就是說:“起來,快去干活,干完活就自己滾蛋,不用回來了”。

Bound(綁定的) 當應用組件通過調用 bindService() 綁定到Service時,Service即處於“綁定”狀態.一個綁定的Service提供客戶端/服務器接口允許組件和Service交互,甚至跨進程操作使用進行間通信(IPC).僅當與另一個應用組件綁定時,綁定服務才會運行.多個組件可以同時綁定到該服務,但全部取消綁定後,該服務即會被銷毀.一個形象的比喻bindService,//這種方式,就是說:“起來,快去干活,有事電話聯系”。

Service的使用

先使用startService()啟動Service。

新建一個MyService繼承Service,並重寫一些所需方法
package com.servicedemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

/**
 * Created by YK on 2016/8/26.
 */
public class MyService extends Service {
    //當其他組件調用bindService()方法請求綁定Service時,該方法被回調。(默認為null)
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("kaka", "onBind");
        return null;
    }
    //當Service第一次創建時,回調該方法(只一次)。
    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("kaka", "onCreate");
    }
    //當其他組件調用startService()方法請求啟動Service時,該方法被回調(每次)。
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("kaka", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    //當Service被銷毀時回調,在該方法中應清除一些占用的資源。
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("kaka", "onDestroy");
    }
}
Service是Android四大基本組件之一,那麼就必須在AndroidManifest.xml中注冊
 
        
            
                

                
            
        
        
    
在MainActivity中加入啟動Service和停止Service的方法
package com.servicedemo;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button mStartService, mStopService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        mStartService.setOnClickListener(this);
        mStopService.setOnClickListener(this);
    }

    private void initView() {
        mStartService = (Button) findViewById(R.id.start);
        mStopService = (Button) findViewById(R.id.stop);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start:
                Intent startIntent = new Intent(this, MyService.class);
                startService(startIntent);
                break;
            case R.id.stop:
                Intent stopIntent = new Intent(this, MyService.class);
                stopService(stopIntent);
                break;
            default:
                break;
        }
    }
}

運行結果及說明:

點擊啟動服務按鈕,運行結果: \ \ 在上面基礎之上點擊啟動服務按鈕兩次: \ 按返回鍵後再次點擊啟動服務按鈕: \ 在上面基礎上再點兩次啟動服務按鈕: \ 點擊停止服務: \ \ 重新進入應用,點擊啟動服務按鈕: \

結論:Service只有在第一次啟動的時候才會調用onCreate()方法,此後再次點擊Start Service按鈕只會執行onStartCommand()方法,並不會再啟動一個服務。

如何讓Activity與Service產生“羁絆"呢?這裡就需要使用之前MyService中沒有使用的方法onBind()了,這個方法就是用於與Activity建立關聯的。先修改MyService的代碼,新增了一個MyBinder類繼承自Binder類,MyBinder中添加了一個doSomething()方法用於執行你要執行的後台任務。

使用bindService()方法啟動Service。

MyService代碼如下:
package com.servicedemo;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

/**
 * Created by YK on 2016/8/26.
 */
public class MyService extends Service {
    //當其他組件調用bindService()方法請求綁定Service時,該方法被回調。(默認為null)
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("kaka", "onBind");
        return new MyBinder();
    }

    //當Service第一次創建時,回調該方法(只一次)。
    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("kaka", "onCreate");
    }

    //當其他組件調用startService()方法請求啟動Service時,該方法被回調(每次)。
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("kaka", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("kaka", "onUnbind");
        return super.onUnbind(intent);
    }

    //當Service被銷毀時回調,在該方法中應清除一些占用的資源。
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("kaka", "onDestroy");
    }

    class MyBinder extends Binder {
        public void doSomething(){
            Log.e("kaka","doSomething");
        }
    }
}
  • 修改MainActivity中的代碼,讓MainActivity和MyService之間建立關聯:
    package com.servicedemo;
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.IBinder;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        private Button mStartService, mStopService;
        private MyService.MyBinder myBinder;
        private ServiceConnection conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e("kaka", "onServiceConnected");
                myBinder = (MyService.MyBinder) service;
                myBinder.doSomething();
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e("kaka", "onServiceDisconnected");
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            mStartService.setOnClickListener(this);
            mStopService.setOnClickListener(this);
        }
    
        private void initView() {
            mStartService = (Button) findViewById(R.id.start);
            mStopService = (Button) findViewById(R.id.stop);
        }
    
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.start:
                    //這裡傳入BIND_AUTO_CREATE,表示在Activity和Service關聯後自動創建Service,
                    //這會使得MyService中的onCreate()方法得到執行,但onStartCommand()方法不會執行。
                    Intent startIntent = new Intent(this, MyService.class);
                    bindService(startIntent, conn, BIND_AUTO_CREATE);
                    break;
                case R.id.stop:
                    //如果沒有綁定服務就執行unbindService是會報錯, java.lang.IllegalArgumentException: Service not registered:
                    unbindService(conn);
                    break;
                default:
                    break;
            }
        }
    }
    

    運行結果及說明:

點擊綁定服務: \ 再次點擊綁定服務:不會打印任何log 沒有點擊解綁的情況下按返回鍵退出打印log: \ 可以看出會提示沒有解綁,但是仍然會執行onUnbind()和onDestroy()方法。 解決的辦法就是:
@Override
    protected void onDestroy(){
        super.onDestroy();
        unbindService(conn);
    }
點擊綁定服務後再點擊解綁: \ 我們可以知道,多次點擊綁定服務按鈕,並不會重復執行ServiceConnection中的onServiceConnected()方法。

如果擊了Start Service按鈕,又點擊了Bind Service按鈕又是什麼情況呢?

這時無論單獨點擊停止按鈕還是解綁服務按鈕,Service都不會被銷毀,必須要將兩個按鈕都點擊一下,Service才會被銷毀,也就是調用onDestroy()方法。所以,一個Service必須要在既沒有和任何Activity關聯又處於停止狀態的時候才會被銷毀。

Service生命周期總結

不同的啟動方式,Service回調的生命周期方法也不一樣。 \

1). 被啟動的服務的生命周期:如果一個Service被某個Activity 調用 Context.startService 方法啟動,那麼不管是否有Activity使用bindService綁定或unbindService解除綁定到該Service,該Service都在後台運行。如果一個Service被startService 方法多次啟動,那麼onCreate方法只會調用一次,onStartCommand將會被調用多次(對應調用startService的次數),並且系統只會創建Service的一個實例(因此你應該知道只需要一次stopService調用)。該Service將會一直在後台運行,而不管對應程序的Activity是否在運行,直到被調用stopService,或自身的stopSelf方法。當然如果系統資源不足,android系統也可能結束服務。

2). 被綁定的服務的生命周期:如果一個Service被某個Activity 調用 Context.bindService 方法綁定啟動,不管調用 bindService 調用幾次,onCreate和onBind方法都只會調用一次,同時onStartCommand方法始終不會被調用。當連接建立之後,Service將會一直運行,除非調用Context.unbindService 斷開連接或者之前調用bindService 的 Context 不存在了(如Activity被finish的時候),系統將會自動停止Service,對應onDestroy將被調用。

3). 被啟動又被綁定的服務的生命周期:如果一個Service又被啟動又被綁定,則該Service將會一直在後台運行。並且不管如何調用,onCreate始終只會調用一次,對應startService調用多少次,Service的onStartCommand便會調用多少次。調用unbindService將不會停止Service,而必須調用 stopService 或 Service的 stopSelf 來停止服務。

4). 當服務被停止時清除服務:當一個Service被終止(1、調用stopService;2、調用stopSelf;3、不再有綁定的連接(沒有被啟動))時,onDestroy方法將會被調用,在這裡你應當做一些清除工作,如停止在Service中創建並運行的線程。

注意:1、使用startService的時候,一般在onStartCommand()中寫主要的邏輯代碼,但是Service運行在主線程中,所以Service本身不能做耗時操作。如果做耗時操作或訪問網絡可以使用異步任務、子線程、Loader等來處理。

特別注意:

1、你應當知道在調用 bindService 綁定到Service的時候,你就應當保證在某處調用 unbindService 解除綁定(盡管 Activity 被 finish 的時候綁定會自動解除,並且Service會自動停止);

2、你應當注意 使用 startService 啟動服務之後,一定要使用 stopService停止服務,不管你是否使用bindService;

3、同時使用 startService 與 bindService 要注意到,Service 的終止,需要unbindService與stopService同時調用,才能終止 Service,不管 startService 與 bindService 的調用順序,如果先調用 unbindService 此時服務不會自動終止,再調用 stopService 之後服務才會停止,如果先調用 stopService 此時服務也不會終止,而再調用 unbindService 或者 之前調用 bindService 的 Context 不存在了(如Activity 被 finish 的時候)之後服務才會自動停止;

4、當在旋轉手機屏幕的時候,當手機屏幕在“橫”“豎”變換時,此時如果你的 Activity 如果會自動旋轉的話,旋轉其實是 Activity 的重新創建,因此旋轉之前的使用 bindService 建立的連接便會斷開(Context 不存在了),對應服務的生命周期與上述相同。

 

\

 

5、在 sdk 2.0 及其以後的版本中,對應的 onStart 已經被否決變為了 onStartCommand,不過之前的 onStart仍然有效。這意味著,如果你開發的應用程序用的 sdk 為 2.0 及其以後的版本,那麼你應當使用 onStartCommand 而不是 onStart。

 

Service和Thread的區別

都是後台運行,那麼Service和Thread的選擇就會迷惑不少人,什麼時候用Service?什麼時候用Thread?

區別:

Service是運行在主線程裡的,而Thread是開辟新的線程。(即如果在Service裡編寫了非常耗時的代碼,程序必定會出現ANR的) Service後台是指它的運行完全不依賴UI,即使Activity被銷毀,或者程序被關閉,只要進程還在,Service就可以運行。(耗時的操作可以在Service中創建一個子線程去處理即可) 既然都要創建子線程,為什麼不在Activity中創建而是到Service中?

這是因為Activity很難對Thread進行控制,當Activity被銷毀後,就無法獲取到Thread的實例了,而且在1Activity中創建的線程,在2Activity中是無法對其進行操作的。但Service不受這些約束,只要進程還在,Activity即使被銷毀了,也能對子線程進行控制。

前台Service

Service基本都是後台運行的,既然後台運行有時候希望它能一直運行,比如時鐘,或者需要實時運行的服務。但是Service的系統優先級還是比較低的,當系統出現了內存不足的情況,還是有可能回收掉正在後台運行的Service,Service如果要防止盡可能不被系統殺掉,需要設置為在前台運行。就像QQ一樣,會在系統狀態欄一直顯示一個圖標信息。

由於設置前台運行service的方法在2.0之前和2.0之後有所變化。所以需要根據不同的版本進行區分;或者完全使用反射機制來處理,這樣只要有相應的方法就可以使用,否則使用其他版本的方法。修改MyService中的代碼為:

  待續。。。

IntentService

待續。。。

 

擁有service的進程具有較高的優先級

 

官方文檔告訴我們,Android系統會盡量保持擁有service的進程運行,只要在該service已經被啟動(start)或者客戶端連接(bindService)到它。當內存不足時,需要保持,擁有service的進程具有較高的優先級。

如果service正在調用onCreate,onStartCommand或者onDestory方法,那麼用於當前service的進程則變為前台進程以避免被killed。如果當前service已經被啟動(start),擁有它的進程則比那些用戶可見的進程優先級低一些,但是比那些不可見的進程更重要,這就意味著service一般不會被killed.如果客戶端已經連接到service (bindService),那麼擁有Service的進程則擁有最高的優先級,可以認為service是可見的。如果service可以使用startForeground(int, Notification)方法來將service設置為前台狀態,那麼系統就認為是對用戶可見的,並不會在內存不足時killed。如果有其他的應用組件作為Service,Activity等運行在相同的進程中,那麼將會增加該進程的重要性。

參考資料:

官方文檔:https://developer.android.com/reference/android/app/Service.html

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