Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Service的理解和使用

Service的理解和使用

編輯:關於Android編程

下面結合我對這一部分的學習,自己做一個小節。
Android5.0之後組件必須使用顯示intent來啟動,如果為隱示的,則設置Intent的包名。intent.setPackage(“com.llay.admin.mydemo”);

一、基本概念

Service是一個應用程序組件,它能夠在後台執行一些耗時較長的操作,並且不提供用戶界面。服務能被其它應用程序的組件啟動,即使用戶切換到另外的應用時還能保持後台運行。此外,應用程序組件還能與服務綁定,並與服務進行交互,甚至能進行進程間通信(IPC)。 比如,服務可以處理網絡傳輸、音樂播放、執行文件I/O、或者與content provider進行交互,所有這些都是後台進行的。

二、服務的分類

服務有以下兩種基本類型:

Started (這裡有一個service的子類IntentService,待會兒單獨講解)
如果一個應用程序組件(比如一個activity)通過調用startService()來啟動服務,則該服務就是被“started”了。一旦被啟動,服務就能在後台一直運行下去,即使啟動它的組件已經被銷毀了。
通常,started的服務執行單一的操作並且不會向調用者返回結果。比如,它可以通過網絡下載或上傳文件。當操作完成後,服務應該自行終止。

Bound
如果一個應用程序組件通過調用bindService()綁定到服務上,則該服務就是被“bound”了。bound服務提供了一個客戶端/服務器接口,允許組件與服務進行交互、發送請求、獲取結果,甚至可以利用進程間通信(IPC)跨進程執行這些操作。綁定服務的生存期和被綁定的應用程序組件一致。
多個組件可以同時與一個服務綁定,不過所有的組件解除綁定後,服務也就會被銷毀。

三、一些回調方法

服務的生命周期圖:

這裡寫圖片描述vcq9tcS3/s7xyrGjrM+1zbO9q7vhtffTw7G+t723qKGjINK7tamxvre9t6jWtNDQo6y3/s7xvs2xu8b0tq+jrLKi1Nq688yo0rvWsdTL0NDPwsiloaMgyOe5+8TjtcS0+sLryrXP1sHLsb63vbeoo6zE477N09DU8MjO1NrN6rPJuaTX97rzzai5/bX308NzdG9wU2VsZigpu/JzdG9wU2VydmljZSgp1tXWubf+zvGhoyCjqMjnufvE49a7z+vM4bmpYmluZLe9yr2jrMTHvs2yu9Do0qrKtc/Wsb63vbeooaOjqTwvcD4NCjxwPm9uQmluZCgpPGJyIC8+DQq1scbky/zX6bz+0OjSqs2ouf1iaW5kU2VydmljZSgpsPO2qLf+zvHKsaOoscjI59a00NBSUEOjqaOsz7XNs7vhtffTw7G+t723qKGjINTasb63vbeotcTKtc/WtPrC69bQo6zE47HY0Ou3tbvYSUJpbmRlcsC0zOG5qdK7uPa907/ao6y/zbuntsvTw8v8wLS6zbf+zvG9+NDQzajQxaGjIMTjsdjQ68i3saPKtc/Wsb63vbeoo6yyu7n9vNnI58TjsrvQ6NKqzOG5qbDztqijrMTHvs23tbvYbnVsbLy0v8mhozwvcD4NCjxwPm9uQ3JlYXRlKCk8YnIgLz4NCrWxt/7O8bXa0ru0zrG7tLS9qMqxo6zPtc2zu+G199PDsb63vbeoo6zTw9Pa1rTQ0NK7tM7Q1LXExeTWw7mk1/ejqNaux7DS0bX308O5/W9uU3RhcnRDb21tYW5kKCm78m9uQmluZCgpKSDBy6GjyOe5+7f+zvHS0b6t1MvQ0KOs1PKxvre9t6i+zbK7u+Gxu7X308OhozwvcD4NCjxwPm9uRGVzdHJveSgpPGJyIC8+DQq1sbf+zvHTw7K7yc/By7Ki0qqxu8/6u9nKsaOsz7XNs7vhtffTw7G+t723qKGjIMTjtcS3/s7x06a4w8q1z9axvre9t6jAtL340NDXytS0tcTH5cDtuaTX96Os1u7I58/fs8yhotLR16Ky4bXE1ezM/cb3bGlzdGVuZXK6zb3TytXG93JlY2VpdmVytci1yKGjINXivavKx7f+zvHK1bW9tcTX7rrz0ru49rX308OhozwvcD4NCjxwPsjnufvX6bz+zai5/bX308NzdGFydFNlcnZpY2UoKaOo1eK74bW81sJvblN0YXJ0Q29tbWFuZCgptcS199PDo6nG9Lavwcu3/s7xo6zEx8O0t/7O8b2r0rvWsbGjs9bUy9DQo6zWsdbB19TQ0NPDc3RvcFNlbGYoKdbV1rm78tPJxuTL/NfpvP6199PDc3RvcFNlcnZpY2UoKcC01tXWucv8oaM8L3A+DQo8cD7I57n71+m8/rX308NiaW5kU2VydmljZSgpwLS0tL2ot/7O8aOoxMdvblN0YXJ0Q29tbWFuZCgpvs2yu7vhsbu199PDo6mjrNTyt/7O8bXEyfq05sbavs3T67G7sPO2qLXE1+m8/tK71sKho9K7tanL+dPQv827p7bLtry21Lf+zvG94rP9wcuw87aoo6zPtc2zvs274c/6u9m4w7f+zvGhozwvcD4NCjxwPr32tbHE2rTmydm1w7/Jwa+hosfSsdjQ67iyuMfTtdPQ08O7p725teO1xGFjdGl2aXR5tcTPtc2z18rUtMqxo6xBbmRyb2lkz7XNs7LFu+HHv9DQ1tXWudK7uPa3/s7xoaMgyOe5+7f+zvGxu9O109DTw7unvbm147XEYWN0aXZpdHmw87ao18WjrNTyy/zSu7Djsru74bG7ybHLwKGjIMjnufu3/s7xyfnD986qI9Tax7DMqNTL0NC3/s7xo6jPws7EzNbC26Opo6zU8sv8vLi69dPA1Layu7vhsbvJscvAoaMgt/HU8qOsyOe5+7f+zvHS0bG7xvS2r7Kix9LS0dTL0NDBy7rcs6TKsbzko6zEx8O0z7XNs72ru+HL5sqxvOTNxtLGtvi9tbXNy/zU2rrzzKjIzs7xwdCx7dbQtcS8trHwo6wgtMvA4Lf+zvG9q7rc09C/ycTcu+Gxu8mxy8AmbWRhc2g7Jm1kYXNoO8jnufu3/s7x0tG+rcb0tq+jrMTHxOOx2NDrusO6w8novMa0+sLro6zKucbkxNzN6sPAtdjTpri2sbvPtc2z1tjG9LXEx+m/9qGjIMjnufvPtc2zybHLwMHLxOO1xLf+zvGjrNa70qrXytS01Nm2yLm708OjrM+1zbO+zbvh1Nm0zsb0tq+3/s7xo6i1sci71eK7ucihvvbT2m9uU3RhcnRDb21tYW5kKCm1xLe1u9jWtaOsz8LOxL2ru+HK9rywo6mho7nY09rPtc2zv8nE3Lvh1Nq6zsqxz/q72bf+zvG1xM/qz7jQxc+io6zH67LO1MS9+LPMus3P37PMoaM8L3A+DQo8aDIgaWQ9"四代碼的實現">四、代碼的實現

注意:Service是Android的四大組件,所以記得在Androidmanifests.xml文件中注冊。注意:四大組件只有BroadCast能動態注冊。

界面圖片:
這裡寫圖片描述

4、1 Started服務的代碼實現

started服務是指其它組件通過調用startService()來啟動的服務,這會引發對該服務onStartCommand()方法的調用。

一旦服務被啟動started,它就擁有了自己的生命周期,這是獨立於啟動它的組件的。並且它能夠在後台一直運行下去,即使啟動它的組件已被銷毀 也是如此。 因此,服務應該能夠在完成工作後自行終止,通過調用stopSelf()即可,或者由其它組件通過調用stopService()也可以。

諸如activity之類的應用程序組件,可以通過調用startService()啟動服務,並傳入一個給出了服務和服務所需數據的Intent對象。服務將在onStartCommand()方法中接收到該Intent對象。

舉個例子,假定某activity需要把一些數據保存到在線數據庫中。此activity可以啟動一個守護服務並通過傳入startService()一個intent把需要保存的數據發送給該服務。該服務在onStartCommand()內接收intent,連接Internet,再進行數據庫事務處理。當事務完成後,服務自行終止,並被系統銷毀。

在Activity中代碼的方法:

private void initStartedService() {
        final Intent intent = new Intent(ServiceDemoActivity.this, HelloStartedService.class);
        startedService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(startedServiceTAG, "開啟服務");
                intent.putExtra("StartedServiceTest", "StartedServiceTest");
                startService(intent);
            }
        });

        stopService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(stopServiceTAG, "手動停止服務");
                stopService(intent);
            }
        });
    }

HelloStartedService.java文件

public class HelloStartedService extends Service {
    private Looper looper;
    private StartedServiceHandler startedServiceHandler;
    public String s;
    public final String TAG = "startedService";

    public HelloStartedService() {
    }

    @Override
    public void onCreate() {
        //這裡配置一些信息
        //啟動運行服務的線程。
        //請記住我們要創建一個單獨的線程,因為服務通常運行於進程的主線程中,可我們不想阻塞主線程。
        //我們還要賦予它後台運行的優先級,以便計算密集的工作不會干擾我們的UI。
        HandlerThread handlerThread = new HandlerThread("StartedService");
        handlerThread.start();
        //獲得HandlerThread的Looper隊列並用於Handler
        looper = handlerThread.getLooper();
        startedServiceHandler = new StartedServiceHandler(looper);
        Log.e(TAG, "onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand");
        s = intent.getStringExtra("StartedServiceTest");
        //對於每一個啟動請求,都發送一個消息來啟動一個處理
        //同時傳入啟動ID,以便任務完成後我們知道該終止哪一個請求。
        Message message = startedServiceHandler.obtainMessage();
        message.arg1 = 1;
        startedServiceHandler.sendMessage(message);
        //如果我們被殺死了,那從這裡返回之後被重啟
        return START_STICKY;

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy");
    }

    public class StartedServiceHandler extends Handler {
        public StartedServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            //通常我們在這裡執行一些工作,比如下載文件。
            try {
                Log.e(TAG, "開始在服務中處理信息");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //根據startId終止服務,這樣我們就不會在處理其它工作的過程中再來終止服務
            //如果組件通過調用startService()(這會導致onStartCommand()的調用)啟動了服務,那麼服務將一直保持運行,直至自行用stopSelf()終止或由其它組件調用stopService()來終止它。
            //如果組件調用bindService()來創建服務(那onStartCommand()就不會被調用),則服務的生存期就與被綁定的組件一致。一旦所有客戶端都對服務解除了綁定,系統就會銷毀該服務。
            stopSelf(msg.arg1);
            Log.e(TAG, "startedService處理數據完成後自動停止");
        }
    }


    //不支持綁定,所以我們返回null
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return null;
    }
}

注意:我這裡在StartedService中,使用了HandlerThread來實現在線程中處理事件。因為這裡的服務還是在主線程中的,不能阻塞。

AndroidManifests.xml文件中注冊

運行圖:
打開服務:
這裡寫圖片描述
關閉服務(完成後自動關閉):
在處理過程後,有這樣一句代碼,自動關閉。
這裡寫圖片描述
這裡寫圖片描述
關閉服務(手動關閉):
在按鈕點擊事件中,手動關閉。
這裡寫圖片描述
這裡寫圖片描述

4、1、1 IntentService代碼實現

注意:默認情況下,運行服務的進程與應用程序的相同,並且運行在應用程序的主線程中。
因此,如果你的服務要執行計算密集或阻塞的操作,而同時用戶又需要與同一個應用程序中的activity進行交互,那麼服務將會降低activity的性能。
為了避免對應用程序性能的影響,你應該在服務中啟動一個新的線程。

IntentService:異步處理服務,新開一個線程:handlerThread在線程中發消息,然後接受處理完成後,會清理線程,並且關掉服務。
IntentService有以下特點:

(1) 它創建了一個獨立的工作線程來處理所有的通過onStartCommand()傳遞給服務的intents。

(2) 創建了一個工作隊列,來逐個發送intent給onHandleIntent()。

(3) 不需要主動調用stopSelft()來結束服務。因為,在所有的intent被處理完後,系統會自動關閉服務。

(4) 默認實現的onBind()返回null

(5) 默認實現的onStartCommand()的目的是將intent插入到工作隊列中

Activity中的方法代碼:

 private void initIntentService() {
        //Android5.0之後必須使用顯示intent來啟動
        //Android的四大組件,只有BroadcastReceiver能夠動態在AndroidManifests文件中注冊
        final Intent intent = new Intent("com.llay.admin.service.action.llay").setPackage("com.llay.admin.mydemo");
        startedIntentService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(startedServiceTAG, "開啟Intent服務");
                intent.putExtra("IntentServiceTest", "IntentServiceTest");
                startService(intent);
            }
        });
        stopIntentService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(stopServiceTAG, "手動Intent停止服務");
                stopService(intent);
            }
        });
    }

HelloIntentService.java文件

public class HelloIntentService extends IntentService {

    /**
     * 構造方法是必需的,必須用工作線程名稱作為參數
     * 調用父類的[http://developer.android.com/reference/android/app/IntentService.html#IntentService(java.lang.String) IntentService(String)]構造方法。
     */
    public HelloIntentService() {
        super("HelloIntentService");
    }


    /**
     * IntentService從缺省的工作線程中調用本方法,並用啟動服務的intent作為參數。
     * 本方法返回後,IntentService將適時終止這個服務。
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        String s = intent.getStringExtra("IntentServiceTest");
        Log.e("IntentServiceTest", s);
    }
}

AndroidManifests.xml文件中注冊

運行圖:
開啟Intent服務:
這裡寫圖片描述
關閉Intent服務(這裡和上面started服務一樣,可以自動關閉,可以手動關閉):
這裡寫圖片描述
注意:這裡我們來分析一下Service和IntentService的區別和使用地方。

利用IntentService來實現一個started服務非常簡單。 不過,假如你的服務需要多線程運行(而不是通過一個工作隊列來處理啟動請求),那你可以擴展Service類來完成每個intent的處理。如你所見,它要干的事情比用IntentService時多了很多。不過,因為是自行處理每個onStartCommand()調用,你可以同時處理多個請求。 本例中沒有這麼去實現,但只要你願意,你就可以為每個請求創建一個新的線程,並立即運行它們(而不是等待前一個請求處理完畢)。

總結: 在當需要一起在線程(自己開)中處理,則使用Started服務。 在當需要一個一個在線程(自帶)中處理,則使用IntentService。

4、2 Bound服務的代碼實現

Activity中的方法代碼:

private void initBoundService() {
        boundService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(boundServiceTAG, "綁定服務");
                //綁定到LocalService
                Intent intent = new Intent(ServiceDemoActivity.this, HelloBoundService.class);
                bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
                Log.e("BoundServiceTest", "BoundServiceStart");
            }
        });

        unboundService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(unBoundServiceTAG, "手動解除綁定");
                unbindService(serviceConnection);
                mBound = false;
            }
        });

        showNumber.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mBound) {
                    //調用LocalService中的方法。
                    //不過,如果該調用會導致某些操作的掛起,那麼調用應該放入單獨的線程中進行,
                    //以免降低activity的性能。
                    int num = helloBoundService.getRandomNumber();
                    Toast.makeText(ServiceDemoActivity.this, "number: " + num, Toast.LENGTH_SHORT).show();
                }
            }
        });

        serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //我們已經綁定到LocalService了,對IBinder進行類型轉換(cast)並獲得LocalService對象的實例
                HelloBoundService.LocalBinder localBinder = (HelloBoundService.LocalBinder) service;
                helloBoundService = localBinder.getHelloBoundService();
                mBound = true;
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                mBound = false;
            }
        };
    }


    //以下是綁定服務的一些回調函數
    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        //與服務解除綁定
        if (mBound) {
            unbindService(serviceConnection);
            mBound = false;
        }
        Log.e(unBoundServiceTAG, "自動解除綁定");
    }

HelloBoundService.java文件:

public class HelloBoundService extends Service {
    //給客戶端的Binder
    public IBinder mBinder = new LocalBinder();
    //生成隨機數
    private final Random mGenerator = new Random();


    /**
     * 用於客戶端Binder的類。
     * 因為知道本服務總是運行於與客戶端相同的進程中,我們就不需要用IPC進行處理。
     */
    public class LocalBinder extends Binder {
        public HelloBoundService getHelloBoundService() {
            return HelloBoundService.this;
        }
    }

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

    /**
     * method for clients
     */
    public int getRandomNumber() {
        return mGenerator.nextInt(100);
    }
}

注意:如果你的服務只用於本地應用程序並且不需要跨進程工作,那你只要實現自己的 Binder
類即可,這樣你的客戶端就能直接訪問服務中的公共方法了。
注意:僅當客戶端和服務位於同一個應用程序和進程中,這也是最常見的情況,這種方式才會有用。比如,一個音樂應用需要把一個activity綁定到它自己的後台音樂播放服務上,采用這種方式就會很不錯。
以下是設置步驟: 1、在你的服務中,創建一個Binder的實例,其中實現以下三者之一:

包含了可供客戶端調用的公共方法 返回當前Service實例,其中包含了可供客戶端調用的公共方法。 或者,返回內含服務類的其它類的一個實例,服務中包含了可供客戶端調用的公共方法。

2、從回調方法onBind()中返回Binder的該實例。
3、在客戶端中,在回調方法onServiceConnected()中接收Binder並用所提供的方法對綁定的服務進行調用。 注意:
服務和客戶端之所以必須位於同一個應用程序中,是為了讓客戶端能夠正確轉換(cast)返回的對象並調用對象的API。
服務和客戶端也必須位於同一個進程中,因為這種方式不能執行任何跨進程的序列化(marshalling)操作。

AndroidManifests.xml文件中注冊

運行圖:
綁定服務:
這裡寫圖片描述
解絆服務:
這裡寫圖片描述

五、擴展

創建一個支持綁定的服務時,你必須提供一個 IBinder ,用作客戶端和服務間進行通信的編程接口。定義這類接口的方式有三種:

擴展Binder類
如果服務是你的應用程序所私有的,並且與客戶端運行於同一個進程中(通常都是如此),你應該通過擴展Binder類來創建你的接口,並從onBind()返回一個它的實例。客戶端接收該Binder對象並用它來直接訪問Binder甚至Service中可用的公共(public)方法。
如果你的服務只是為你自己的應用程序執行一些後台工作,那這就是首選的技術方案。不用這種方式來創建接口的理由只有一個,就是服務要被其它應用程序使用或者要跨多個進程使用。

使用Messenger
如果你需要接口跨越多個進程進行工作,可以通過Messenger來為服務創建接口。在這種方式下,服務定義一個響應各類消息對象Message的Handler。此Handler是Messenger與客戶端共享同一個IBinder的基礎,它使得客戶端可以用消息對象Message向服務發送指令。此外,客戶端還可以定義自己的Message,以便服務能夠往回發送消息。
這是執行進程間通信(IPC)最為簡便的方式,因為Messenger會把所有的請求放入一個獨立進程中的隊列,這樣你就不一定非要把服務設計為線程安全的模式了。

使用AIDL
Android接口定義語言AIDL(Android Interface Definition Language)完成以下的所有工作:將對象解析為操作系統可識別的原始形態,並將它們跨進程序列化(marshal)以完成IPC。前一個使用Messenger的方式,實際上也是基於AIDL的,它用AIDL作為底層結構。如上所述,Messenger將在一個單獨的進程中創建一個包含了所有客戶端請求的隊列,這樣服務每次就只會收到一個請求。可是,如果想讓你的服務能同時處理多個請求,那你就可以直接使用AIDL。這種情況下,你的服務必須擁有多線程處理能力,並且是以線程安全的方式編寫的。
要直接使用AIDL,你必須創建一個.aidl文件,其中定義了編程的接口。Android SDK工具使用此文件來生成一個抽象類(abstract class),其中實現了接口及對IPC的處理,然後你就可以在自己的服務中擴展該類。

注意:

絕大多數應用程序都不應該用AIDL來創建bound服務,因為這可能需要多線程處理能力並且會讓代碼變得更為復雜。
因此,AIDL對絕大多數應用程序都不適用,並且本文也不會討論如何在服務中使用它的內容。如果你確信需要直接使用AIDL,那請參閱 AIDL
文檔。

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