Android:Service詳解

weixin_34185560發表於2017-04-12

這篇文章主要是講解Service

前言:Service非常適用於去執行那些不需要和使用者互動而且還要長期執行在後臺的任務。Service預設執行緒為UI執行緒,不要在Service中執行耗時的操作,除非你在Service中建立了子執行緒來完成耗時操作.
Service的執行不依賴於任何使用者介面,即使程式被切換到後臺或者使用者開啟另一個應用程式,Service仍然能夠保持正常執行,這也正是Service的使用場景。當某個應用程式程式被殺掉時,所有依賴於該程式的Service也會停止執行.

一、Service的基本用法

普通的Service的生命週期很簡單,分別為onCreate、onStartCommand、onDestroy這三個。當我們startService()的時候,首次建立Service會回撥onCreate()方法,然後回撥onStartCommand()方法,再次startService()的時候,就只會執行onStartCommand()。服務一旦開啟後,我們就需要通過stopService()方法或者stopSelf()方法關閉服務,這時就會回撥onDestroy()

  • 建立服務類

建立一個服務非常簡單,只要繼承Service,並實現onBind()方法

public class BackGroupService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        Log.e("Service","onBind");
        return null;
    }

    @Override
    public void onCreate() {
        Log.e("Service","onCreate");
        super.onCreate();
    }

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

    @Override
    public void onDestroy() {
        Log.e("Service","onDestroy");
        super.onDestroy();
    }
}
  • 配置AndroidManifest
    Service也是四大元件之一,所以必須在AndroidManifest中配置
    <service android:name=".BackGroupService" />
  • 啟動服務和停止服務
    我們通過兩個按鈕分別演示啟動服務和停止服務,通過startService()開啟服務,通過stopService()停止服務
Intent startServiceIntent = new Intent(this, BackGroupService.class);
startService.setOnClickListener((v)-> {
                //啟動服務
                startService(startServiceIntent );
        });
        stopService.setOnClickListener((v)-> {
                //停止服務
                stopService(startServiceIntent);
        });

執行程式後,我們點選開始服務,然後關閉服務。我們以Log資訊來驗證普通Service的生命週期:onCreate->onStartCommand->onDestroy
當你開啟服務後,還有一種方法可以關閉服務,在設定中,通過應用->找到自己應用->停止

3846387-fc23c08becfb6174.jpg

二、Service和Activity進行通訊

Service和Activity進行通訊指前臺頁面(Activity)可以呼叫後臺服務的方法,實現步驟是和普通服務實現步驟是一樣的,區別在於啟動的方式和獲得Service的代理物件

  • 建立服務類

這裡和普通Service不同,在onBind中返回一個Binder 物件,Activity可以獲取該Binder 物件從而執行後臺服務的方法.

class MyBind extends Binder {
    public void startDownload() {
        Log.d("Service", "開始下載");
        // 執行具體的下載任務
    }
}
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("Service", "onBind");
        return new MyBind(); //這裡返回服務代理物件
    }
    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("Service", "onUnbind");
        return super.onUnbind(intent);
    }
  • 繫結服務和解除繫結服務
    我們通過兩個按鈕分別演示繫結服務和解除繫結服務,通過bindService()開啟服務,通過unbindService()停止服務
bindService.setOnClickListener((view)-> {
                bindService(startServiceIntent,serviceConnection,BIND_AUTO_CREATE);
        });
unbindService.setOnClickListener((view->) {
         unbindService(serviceConnection);
        });
  }
    /**
     * 建立ServiceConnection
     */
    final ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //拿到後臺服務代理物件
            final MyBind myBind = (MyBind) iBinder;
            myBind.startDownload();
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
        }
    };

這裡和startService的區別在於多了一個ServiceConnection物件,該物件是使用者繫結後臺服務後,可獲取後臺服務代理物件的回撥,我們可以通過該回撥,拿到後臺服務的代理物件,並呼叫後臺服務定義的方法,也就實現了後臺服務和前臺的互動

  • 執行程式碼
    執行程式後,我們點選繫結服務,然後一段時間後解除繫結服務。我們以Log資訊來驗證Service的生命週期:onCreate->onBind->onUnBind->onDestroy,其中onbind開啟服務重複開啟不會執行任何方法,其中也可以看到我們呼叫後臺服務的方法開始下載了....


    3846387-a8ae2b10802bb42e.jpg
  • 混合性互動的後臺服務
    如果我們既點選了Start Service按鈕,又點選了Bind Service按鈕會怎麼樣呢?這個時候你會發現,不管你是單獨點選Stop Service按鈕還是Unbind Service按鈕,Service都不會被銷燬,必要將兩個按鈕都點選一下,Service才會被銷燬。也就是說,點選Stop Service按鈕只會讓Service停止,點選Unbind Service按鈕只會讓Service和Activity解除關聯,一個Service必須要在既沒有和任何Activity關聯又處理停止狀態的時候才會被銷燬。


    3846387-e34c10c5f33a1f5e.jpg

三、Service和Thread的關係
Service和Thread到底有什麼關係呢?什麼時候應該用Service,什麼時候又應該用Thread?
答案是Service和Thread之間沒有任何關係!

之所以有不少人會把它們聯絡起來,主要就是因為Service的後臺概念。Thread我們大家都知道,是用於開啟一個子執行緒,在這裡去執行一些耗時操作就不會阻塞主執行緒的執行。而Service我們最初理解的時候,總會覺得它是用來處理一些後臺任務的,一些比較耗時的操作也可以放在這裡執行,這就會讓人產生混淆了。但是Service其實就是執行在主執行緒裡的,那要Service又有何用呢?其實大家不要把後臺和子執行緒聯絡在一起就行了,這是兩個完全不同的概念。Android的後臺就是指,它的執行是完全不依賴UI的。即使Activity被銷燬,或者程式被關閉,只要程式還在,Service就可以繼續執行。比如說一些應用程式,始終需要與伺服器之間始終保持著心跳連線,就可以使用Service來實現。在Service中再建立一個子執行緒,然後在這裡去處理耗時邏輯就沒問題了。既然在Service裡也要建立一個子執行緒,那為什麼不直接在Activity裡建立呢?這是因為Activity很難對Thread進行控制,當Activity被銷燬之後,就沒有任何其它的辦法可以再重新獲取到之前建立的子執行緒的例項。而且在一個Activity中建立的子執行緒,另一個Activity無法對其進行操作。但是Service就不同了,所有的Activity都可以與Service進行關聯,然後可以很方便地操作其中的方法,即使Activity被銷燬了,之後只要重新與Service建立關聯,就又能夠獲取到原有的Service中Binder的例項。因此,使用Service來處理後臺任務,Activity就可以放心地finish,完全不需要擔心無法對後臺任務進行控制的情況。

四、前臺服務
Service幾乎都是在後臺執行的,一直以來它都是默默地做著辛苦的工作。但是Service的系統優先順序還是比較低的,當系統出現記憶體不足情況時,就有可能會回收掉正在後臺執行的Service。如果你希望Service可以一直保持執行狀態,而不會由於系統記憶體不足的原因導致被回收,就可以考慮使用前臺Service。前臺Service和普通Service最大的區別就在於,它會一直有一個正在執行的圖示在系統的狀態列顯示,下拉狀態列後可以看到更加詳細的資訊,非常類似於通知的效果。當然有時候你也可能不僅僅是為了防止Service被回收才使用前臺Service,有些專案由於特殊的需求會要求必須使用前臺Service.

  • 建立服務類
    前臺服務建立很簡單,其實就在Service的基礎上建立一個Notification,然後使用Service的startForeground()方法即可啟動為前臺服務
private void showNotification() {
        final Notification.Builder mBuilder = new Notification.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("前臺服務")
                .setContentText("我是前臺服務");
        final Intent intent = new Intent(this, MainActivity.class);
        //建立任務棧Builder
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(MainActivity.class);
        stackBuilder.addNextIntent(intent);
        PendingIntent pendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
        //設定跳轉Intent到通知中
        mBuilder.setContentIntent(pendingIntent);
        //獲取通知服務
        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //構建通知
        Notification notification = mBuilder.build();
        //顯示通知
        nm.notify(0, notification);
        //啟動為前臺服務
        startForeground(0, notification);
    }
  • 配置服務
    <service android:name=".ForegroundService" />
  • 啟動和結束服務
    現在重新執行一下程式,並點選Start Service或Bind Service按鈕,ForegroundService就會以前臺Service的模式啟動了,並且在系統狀態列會彈出一個通欄圖示,下拉狀態列後可以看到通知的詳細內容,如下圖所示。


    3846387-1b620edf9219f34a.jpg

五、IntentService
IntentService是專門用來解決Service中不能執行耗時操作這一問題的,建立一個IntentService也很簡單,只要繼承IntentService並覆寫onHandlerIntent函式,在該函式中就可以執行耗時操作了,執行完耗時操作後自動結束服務.

public class TheIntentService extends IntentService {

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

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.e("IntentService", "耗時前");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.e("IntentService", "耗時後");
    }
}

相關文章