Android 四大元件之 Service

Keven發表於2019-04-07

讀前思考

學習一門技術或者看一篇文章最好的方式就是帶著問題去學習,這樣才能在過程中有茅塞頓開、燈火闌珊的感覺,記憶也會更深刻。

  1. 談一談 Service 的生命週期?
  2. Service的兩種啟動方式?區別在哪?
  3. 一個 Activty 先 start 一個 Service 後,再 bind 時會回撥什麼方法?
  4. Service 如何和 Activity 進行通訊?
  5. 是否能在 Service 進行耗時操作?如果非要可以怎麼做?
  6. 前臺服務是什麼?和普通服務的不同?如何去開啟一個前臺服務?

建立服務

  1. 建立一個類繼承 Service ,重寫 onBind( )方法 .
public class Part1aService extends Service {
    private static final String CHANNEL_ID_STRING = "KEVEN_JIANSHU";
    
    public Part1aService() {
    
    }

    @Override
    public IBinder onBind(Intent intent) {
        LogUtils.i("Service 執行了 onBind( )");
        return null;
    }
複製程式碼
  1. 在清單檔案中註冊 Service .
<service android:name=".part1.Part1aService"></service>
複製程式碼

啟動服務

啟動服務有兩種方式,一種是 startService( ) ,一種是 bindService( ) .

startService( )

特點: 一旦服務開啟就跟呼叫者(開啟者)沒有任何關係了。開啟者退出了,開啟者掛了,服務還在後臺長期的執行,開啟者不能呼叫服務裡面的方法。

使用如下程式碼進行服務開啟

Intent intent = new Intent(Part1aActivity.this, Part1aService.class);
startService(intent);
複製程式碼

使用如下程式碼進行服務關閉(或者 Service 呼叫 stopSelf( ) )

Intent intent = new Intent(Part1aActivity.this, Part1aService.class);
stopService(intent);
複製程式碼

生命週期回撥

com.keven.jianshu I/TAG: Service 執行了 onCreate( )
com.keven.jianshu I/TAG: Service 執行了 onStartCommand( )
com.keven.jianshu I/TAG: Service 執行了 onDestroy( )
複製程式碼

Android 8.0 + ,對後臺服務進行了限制了,官網如下所述。

Android 8.0 還對特定函式做出了以下變更:

如果針對 Android 8.0 的應用嘗試在不允許其建立後臺服務的情況下使用 startService() 函式,則該函式將引發一個 IllegalStateException。
新的 Context.startForegroundService() 函式將啟動一個前臺服務。現在,即使應用在後臺執行,系統也允許其呼叫 Context.startForegroundService()。
不過,應用必須在建立服務後的五秒內呼叫該服務的 startForeground() 函式。

所以,啟動程式碼就可以針對文件所述進行適配。

//進行8.0+ 以上啟動服務的適配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    Intent intent = new Intent(Part1aActivity.this, Part1aService.class);
    startForegroundService(intent);
} else {
    Intent intent = new Intent(Part1aActivity.this, Part1aService.class);
    startService(intent);
}
複製程式碼

在 Service 中的 onCreate( ) 方法中做如下修改。

@Override
    public void onCreate() {
        LogUtils.i("Service 執行了 onCreate( )");
        super.onCreate();

        //適配8.0+service
        NotificationManager notificationManager = (NotificationManager) MyApp.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationChannel mChannel = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            mChannel = new NotificationChannel(CHANNEL_ID_STRING, "Keven 簡書", NotificationManager.IMPORTANCE_HIGH);
            notificationManager.createNotificationChannel(mChannel);
            Notification notification = new Notification.Builder(getApplicationContext(), CHANNEL_ID_STRING).build();
            startForeground(1, notification);
        }

    }
複製程式碼

bindService( )

特點:bind 的方式開啟服務,繫結服務,呼叫者掛了,服務也會跟著掛掉。繫結者可以呼叫服務裡面的方法。

使用 bind 方式開啟與關閉服務的程式碼如下

//繫結服務
Intent intent = new Intent(Part1aActivity.this, Part1aService.class);
bindService(intent, connection, BIND_AUTO_CREATE);

//解綁服務
Intent intent = new Intent(Part1aActivity.this, Part1aService.class);
unbindService(connection);
複製程式碼

生命週期呼叫如下

com.keven.jianshu I/TAG: Service 執行了 onCreate( )
com.keven.jianshu I/TAG: Service 執行了 onBind( )
com.keven.jianshu I/TAG: Service 執行了 onUnbind( )
com.keven.jianshu I/TAG: Service 執行了 onDestroy( )
複製程式碼

一個 Activty 先 start 一個 Service 後,再 bind 時會回撥 onBind( ) 方法,不會呼叫 onCreate( ) 方法,因為服務已經啟動了。

Service 和 Activity 進行通訊

方式一:廣播

這種方式比較簡單,用 Android 四大元件之一的廣播即可實現通訊。

方式二:針對 bindService( ) 啟動方式的通訊

  1. 在 Service 中建立類繼承 Binder,並在其中編寫需要 Activity 呼叫的方法。
class MyBinder extends Binder {
        public void getServiceMethod() {
            LogUtils.e("呼叫了 Part1aService 的 getServiceMethod( ) 方法");
        }
    }
複製程式碼
  1. 在 Service 中建立上述類的物件。
private MyBinder binder = new MyBinder();
複製程式碼
  1. 在 Service 中的 onBind( ) 方法中返回建立的物件。
@Override
    public IBinder onBind(Intent intent) {
        LogUtils.i("Service 執行了 onBind( )");
        return binder;
    }
複製程式碼
  1. 在 Activity 中建立 ServiceConnection 類,並重寫它的 onServiceConnected( ) 和 onServiceDisconnected( ) 方法,並將 onServiceConnected( ) 方法中的 IBinder 型別的物件強轉為 Service 中類的型別,之後就可以呼叫其中的方法進行通訊了。
ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Part1aService.MyBinder myBinder = (Part1aService.MyBinder) service;
            myBinder.getServiceMethod();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
複製程式碼

耗時操作

因為服務也是執行在 UI 執行緒的,所以不能再服務中執行耗時操作,如果需要執行耗時操作,可以有如下兩種方式。

方式一: 開啟子執行緒執行耗時操作;

方式二: 使用 IntentService 。

IntentService 是 Android 系統提供的類,可在其中執行耗時任務,當任務執行完後,服務會自動銷燬,無需操作。

通過檢視原始碼可以知道 IntentService 繼承 Service,在它的 onCreate( ) 方法中建立了 HandlerThread,Looper,ServiceHandler ,所以其內部有訊息機制和執行緒,可以在 onHandleIntent( ) 方法中執行耗時操作。

我們可以新建一個類繼承 IntentService,重寫它的 onHandleIntent( ) 方法,並在其中執行耗時操作,然後在清單檔案中註冊這個服務即可。

文章已經讀到末尾了,不知道最初的幾個問題你都會了嗎?如果不會的話?可以再針對不會的問題進行精讀哦!答案都在文中,相信你肯定可以解決的!

相關文章