Android知識點回顧之Service基礎

1、Service的簡介

Service爲Android四大組件之一,和Activity同樣,都是Context的子類,只是它沒有界面,Service很適合去執行那些長時間運行又不須要和用戶交互的任務。因爲Service自己是在主線程運行的,因此若是須要執行耗時操做仍是須要另外開啓子線程,不然會出現ANR錯誤。java

Service包含三種類型:服務器

  • Foreground:前臺服務。啓動的時候使用通知(Notification),以提示用戶此服務正在執行。須要注意的是此時的Service仍是在後臺運行的。
  • Background:後臺服務。執行的時候用戶是不可感知的。
  • Bound:綁定服務。當使用bindService()方法啓動Service的時候,此Service爲綁定服務。此時Service能夠和與它綁定的組件進行交互。能夠綁定多個組件,而且當全部綁定的組件對其解綁時此Service纔會被銷燬。

2、Service的生命週期

Service 兩種啓動模式的生命週期

Service 有兩種啓動方式,startService() 和 bindService()。多線程

  • startService() 啓動的生命週期如上圖左邊所示。特別的,會回調 onStartCommand()方法異步

    • onCreate()在整個生命週期中只會被調用一次
    • onStartCommand()可能會被屢次調用,包括 Service 重啓,重複調用 startService()
    • startService() 啓動的 Service 中止的方法是 Service 調用本身的 stopSelf() 方法或是其餘其餘組件調用 stopService(Intent name) 方法,參數中的 name 能夠直接 new 一個,只要關聯的 class 爲要中止的目標 Service就能夠了。
    • stopSelf()還有個重載方法 stopSelf(int startId),可單獨中止 startId 對應的請求。startId 爲 onStartCommand() 方法傳入的參數 startId,用來區分每次訪問請求,
  • bindService()啓動的生命週期如上圖右邊所示。特別的,會調用 onBind() 和 onUnbind()ide

    • onCreate() 在整個生命週期中只會被調用一次
    • 同一個組件能夠綁定屢次
    • 當多個組件綁定此服務時 onBind() 只在第一次綁定的時候被調用。
    • 用此方式啓動的 Service 使用 Service.stopSelf() 或者是在組件調用 stopService() 來中止服務是無效的
    • 此服務會一直運行下去,直到綁定的全部的組件調用 unbindService() 方法對其解綁,只要有一個綁定的組件未解綁,則此服務不會中止。

onStartCommand() 方法返回一個整數值,讓系統在系統殺死此 Service 的時候如何處理此 Serviceui

  • START_NOT_STICKY:Service 被系統殺死後不會重啓
  • START_STICKY:Service 被系統殺死後會重啓,此時調用 onStartCommand() 傳入的 Intent 的值爲null
  • START_REDELIVER_INTENT:Service 被系統殺死後會重啓,此時調用 onStartCommand() 傳入的 Intent 值爲最後一次調用 startService() 時傳入的。

3、建立服務

一、建立後臺服務(無交互)

不可交互的後臺服務的啓動方式爲 startService(),this

public class MyService extends Service{
    public static final String TAG = "MyService";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG,"onBind");
        return null;
    }

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

    private void runAfterStop(){
        new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    Log.e(TAG,"stopSelf()");
                    stopSelf();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

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

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

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

啓動服務spa

public class BaseActivity extends AppCompatActivity{
        ...
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
        ...
}
複製代碼

二、建立前臺服務

前臺服務會在通知欄/狀態欄上顯示,而且此時的 Service 的優先級比較高,線程

public class ForegroundService extends Service{
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        //id不能爲0,不然通知欄/狀態欄不會顯示
        int id = 1;
        startForeground(id,createNotification());
    }

    private Notification createNotification(){
        Intent notifiIntent = new Intent(this, FirstActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notifiIntent,0);

        Notification.Builder builder = new Notification.Builder(this)
                .setContentTitle("title")
                .setContentText("Content")
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentIntent(pendingIntent);

        return builder.build();
    }
}

...
//啓動服務
public class BaseActivity extends AppCompatActivity{
        ...
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
        ...
}
複製代碼

若是要取消前臺服務,能夠調用 stopForeground(boolean removeNotification) ,代理

參數 removeNotification 爲 true 表示取消前臺服務通知也移除掉通知欄/狀態欄的圖標,false 表示不移除

調用此方法不會致使服務中止,只是把前臺轉到後臺

當服務中止的時候,通知欄/狀態欄的圖標也會同時被移除

三、建立綁定服務(可交互的)

綁定服務(Bound Services)爲客戶端-服務器模式。因爲須要交互,因此須要有一箇中間代理對象,此對象須要服務端建立而後返回給客戶端持有,類型爲 Binder,由 onBind() 進行返回。

服務端代碼:

public class BoundService extends Service {

    private IBinder mBinder = new LocalBinder();

    public class LocalBinder extends Binder{
        public int add(int a,int b){
            return BoundService.this.add(a,b);
        }
    }

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

    private int add(int a,int b){
        return a + b;
    }
}
複製代碼

客戶端代碼:

public class BoundActivity extends Activity{
    //持有綁定服務返回的 Binder 對象
    private BoundService.LocalBinder mBinder;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        serviceBind();
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(mServiceConn );
    }

    //綁定服務
    private void serviceBind() {
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
    }

    //點擊事件
    public void addClick(View view){
        add(2,3);
    }

    //調用綁定服務的方法
    private void add(int a,int b){
        Log.(TAG,mBinder.add(a,b));
    }

    //綁定服務後的回調接口
    private ServiceConnection mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG,"bing Service Connected");
            mBinder = (BoundService.LocalBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG,"unBind Service disConnected");
            mBinder = null;
        }
    };

}
複製代碼

這個只是基於簡單相同進程內調用,若是在 Manifest 文件中給 BoundService 添加上 process=":remote" 變成遠處服務,則上面的代碼就會報錯,拋出 java.lang.ClassCastException 異常,由於此種狀況下返回的是 Binder 的代理對象 BInderProxy,因此拋出類型轉換錯誤。進程間的通訊要用到 AIDL ,這個只能另外寫篇回顧了。

四、IntentService

IntentService 是 Service 的子類,用來處理異步請求 特色: 一、會建立一個默認的工做子線程處理全部的請求 二、會有一個隊列逐個處理全部的 Intent 並會在 onHandleIntent() 實現 三、當 Service 處理全部的工做後會自動結束 Service,不須要手動調用 stopSelf() 四、默認實現返回值爲 null 的 onBind() 方法 五、默認實現 onStartCommand(),會把請求的 Intent 放到工做隊列裏

因此咱們不須要去管理 IntentService 的生命週期和管理線程。而且 IntentService 在處理完因此任務後會自動關閉。當業務不須要涉及到多線程任務時,IntentService 就可以知足大多數的需求了。

實現 IntentService 的方式很簡單,只須要提供一個構造方法和實現 onHandleIntent() 方法

public class TestIntentService extends IntentService {
    publicTestIntentService() {
        super("TestIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        //模擬耗時操做
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
複製代碼

IntentService 的是使用 HandlerThread + Handler 來實現的。HandlerThread 爲Thread的子類,Handler 運行在 HandlerThread 線程中,處理耗時操做。

4、Service 和線程 Thread

Service 是 Android 的一種機制,運行在主線程中,若是進行耗時操做,須要建立一個子線程執行。

Service 的優先級高於後臺掛起的 Activity 和其所建立的子線程 Thread。系統可能會在內存不足的時候,優先殺死後臺掛起的 Activity 或 Thread,而不會輕易殺死 Service。

Thread 的運行是獨立於 Activity 的,當 Activity 被 finish 掉的時候,若是沒有主動中止 Thread 或未執行完任務,那麼它還會繼續執行。此時程序將再也不持有這個 Thread 的引用,此時將控制不了此 Thread。

因此當須要長期穩定的在後臺運行某個任務時,須要使用 Service,而當 Service 在運行這個任務時要處理耗時操做時,要另外建立子線程來執行。

相關文章
相關標籤/搜索