Service介紹
先前在[Android]四大元件簡介中有簡單介紹過,Service是一個沒有使用者介面的程式,一般是拿來做在背景處理一些需要長時間監控的事情。
Service和Activity最大的差別是,他可以在背景進行長時間的工作。
在這裡還是拿音樂播放器為例:
音樂播放器的特色是,就算當Activity已經退到背景去,但音樂仍在繼續播放,且當一首歌播放完之後還會繼續播放下一首,直到你退出這個應用程式。
能夠達到這樣的功能,都是歸功於Service在背景控制音樂播放器的功勞。
Service生命週期
Service如同Activity一樣,也是具備生命週期的。
由於Service沒有畫面,因此不需要onStart()、onStop()、onPause()、onResume()等等callback做相應的處理。
生命週期如下圖:
由上圖我們可以看到,Service具有兩種啟動方式,分別是startServide()和bindService()
startService()
這種方式是透過intent來啟動Service,系統會先呼叫onCreate(),然後將intent帶入onStartCommand方法中。
透過startService()方式啟動的Service會一直執行,直到呼叫了stopService()或是內部呼叫stopSelf()才會被停止。
另外,不論呼叫多少次startService(),只有在Service第一次啟動時才會呼叫onCreate(),其他時間都是直接呼叫onStartCommand()的,因此我們可以利用intent中夾帶一些參數讓Service去做相對應的事情。
生命週期參考左半邊的流程。
startService範例
啟動Service
1.建立一個TestService.java,並繼承自android.app.Service
public class TestService extends Service {
private static final String TAG = "TestService";
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service is call onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service is call onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "Service is call onBind()");
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Service is call onDestroy()");
}
}
2.在MainActivity的onCreate(),透過intent啟動TestService,並在onDestroy(),關閉Service
public class MainActivity extends AppCompatActivity {
private Intent serviceIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceIntent = new Intent(MainActivity.this, TestService.class);
startService(serviceIntent);
}
@Override
protected void onDestroy() {
super.onDestroy();
stopService(serviceIntent);
}
}
傳送指令給Service
回到TestService.java,並在onStartCommand()中,加入下列程式碼,像這樣:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service is call onStartCommand()");
// 接收由 Activity 傳送過來的指令
String message = intent.getStringExtra("SHOW_MESSAGE");
if (message != null) {
Log.i(TAG, "Message is: " + message);
}
return super.onStartCommand(intent, flags, startId);
}
在MainActivity中,加入一個按鈕,並透過先前建立的serviceIntent物件,發送一個String Extra,像這樣:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
serviceIntent.putExtra("SHOW_MESSAGE", "Hello");
startService(serviceIntent);
}
});
執行應用程式,即可在Logcat看到Service被啟動。
按下按鈕後,我們可以在Logcat看到Service接收到SHOW_MESSAGE,並顯示Hello
2021-05-19 17:53:37.751 5586-5586/? I/TestService: Service is call onCreate()
2021-05-19 17:53:37.751 5586-5586/? I/TestService: Service is call onStartCommand()
2021-05-19 17:53:42.070 5586-5586/? I/TestService: Service is call onStartCommand()
2021-05-19 17:53:42.070 5586-5586/? I/TestService: Message is: Hello
bindService()
相對於startService()的另一種啟動方式,則是bindService()。
bindService()的啟動方式是在Activity建立一個與Service綁定的連線(ServiceConnection),讓與之綁定的Activity可以直接呼叫這個Service中的公開方法。
另外,bindService()方式啟動的Service,會隨著Activity與之解綁時被銷毀。因此在設計時必須考慮Service是需要長時間運作而決定要使用startService()還是bindService()。
生命週期參考右半邊的流程。
bindService範例
啟動Service
1.建立一個TestBindService.java,並繼承自android.app.Service。
此外,在TestBindService中,建立一個MyTestBinder類別。
接著再將onBind()覆寫,把MyTestBinder作為回傳的物件傳出。
public class TestBindService extends Service {
private static final String TAG = "TestBindService";
private final IBinder binder = new MyTestBinder();
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service is call onCreate()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "Service is call onBind()");
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "Service is call onUnbind()");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Service is call onDestroy()");
}
public void showMessage(String message) {
Log.i(TAG, "Message is: " + message);
}
public class MyTestBinder extends Binder {
public TestBindService getService() {
return TestBindService.this;
}
}
}
2.在MainActivity的onCreate(),新增connect、mService物件。
並在connect的onServiceConnected()中,透過IBinder取得我們要啟動的Service,並帶到mService。
private TestBindService mService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = ((TestBindService.MyTestBinder) service).getService();
Log.i(TAG, "Service is connected");
}
};
之後我們就可以直接呼叫Service中公開的function,讓Service做事。
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mService.showMessage("Hello");
}
});
執行APP,按下APP上的按鈕,即可在Logcat看到我們呼叫Service所做的事情了
2021-05-19 18:11:48.490 6303-6303/com.ray650128.startservicetest I/MainActivity: Service is connected
2021-05-19 18:11:52.806 6303-6303/com.ray650128.startservicetest I/TestBindService: Message is: Hello
使用Service時可能會遇到的疑問
1.Service可以長時間運作,代表它可以執行耗時工作嗎?
答案是不行,因為Service基本的三個生命周期都是在主執行緒上執行的,因此我們如果要執行耗時工作,還是必須切換到其他執行緒來執行耗時工作。
其他待後續補充
以上是繼四大元件簡介之後,對於Service更詳細一點的介紹,如果需要更詳細的資料,請參考Android Developers網站