1. Vòng đời dịch vụ:
Khi khởi tạo bằng startService(), dịch vụ trải qua chuỗi phương thức: onCreate() → onStartCommand(). Dịch vụ sẽ tiếp tục chạy nền độc lập cho đến khi có lệnh dừng rõ ràng qua stopService() hoặc stopSelf(), lúc đó hệ thống gọi onDestroy(). Ngược lại, với bindService(), dịch vụ chỉ được tạo nếu chưa tồn tại (onCreate()), sau đó lập tức gọi onBind(). Khi tất cả client hủy liên kết (ví dụ Activity kết thúc), hệ thống lần lượt gọi onUnbind() rồi onDestroy() — trừ khi dịch vụ cũng đang được giữ bởi startService().
2. Mô hình tương tác:
startService() phù hợp cho các tác vụ nền không cần phản hồi trực tiếp — như phát nhạc, đồng bộ dữ liệu hoặc gửi thông báo. Nó hoạt động theo kiểu "gửi và quên", không duy trì tham chiếu tới đối tượng Service. Trong khi đó, bindService() thiết lập kênh giao tiếp hai chiều qua IBinder, cho phép client gọi trực tiếp các phương thức của Service (ví dụ: điều khiển phát/tạm dừng nhạc, truy vấn trạng thái). Điều này đòi hỏi quản lý cẩn thận vòng đời liên kết để tránh rò rỉ bộ nhớ.
3. Hành vi kết hợp:
Nếu một Service vừa được startService() vừa bị bindService(), nó sẽ tồn tại cho đến khi cả hai điều kiện đều được đáp ứng: tất cả client đã hủy liên kết và không còn lệnh start nào đang giữ nó. Phương thức onCreate() chỉ được gọi một lần dù có bao nhiêu lần gọi start hay bind. Nếu Service đã được bind trước, việc gọi startService() sau đó sẽ chỉ kích hoạt onStartCommand() mà không gọi lại onCreate(). Tương tự, nếu đã được start trước, bindService() sẽ trả về IBinder ngay lập tức mà không tái tạo Service.
Ví dụ minh họa — Dịch vụ phát âm thanh nền:
Dịch vụ sử dụng startService():
public class AudioPlaybackService extends Service {
private static final String TAG = "AudioSvc";
private MediaPlayer player;
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Dịch vụ được tạo");
player = MediaPlayer.create(this, R.raw.track);
player.setLooping(false);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Bắt đầu phát");
if (!player.isPlaying()) player.start();
return START_STICKY;
}
@Override
public void onDestroy() {
Log.d(TAG, "Dịch vụ bị hủy");
if (player.isPlaying()) player.stop();
player.release();
player = null;
}
@Override
public IBinder onBind(Intent intent) {
return null; // Không hỗ trợ liên kết
}
}Activity điều khiển:
public class PlayerActivity extends AppCompatActivity {
private Button btnStart, btnStop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
btnStart = findViewById(R.id.btn_start);
btnStop = findViewById(R.id.btn_stop);
btnStart.setOnClickListener(v ->
startService(new Intent(this, AudioPlaybackService.class))
);
btnStop.setOnClickListener(v ->
stopService(new Intent(this, AudioPlaybackService.class))
);
}
}Dịch vụ hỗ trợ liên kết (bindService()):
public class InteractiveAudioService extends Service {
private final IBinder binder = new LocalBinder();
private MediaPlayer player;
public class LocalBinder extends Binder {
public InteractiveAudioService getService() {
return InteractiveAudioService.this;
}
}
@Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.track);
player.setLooping(false);
}
@Override
public IBinder onBind(Intent intent) {
Log.d("InteractiveSvc", "Đã liên kết");
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("InteractiveSvc", "Hủy liên kết");
return super.onUnbind(intent);
}
public void play() {
if (player != null && !player.isPlaying()) player.start();
}
public void pause() {
if (player != null && player.isPlaying()) player.pause();
}
@Override
public void onDestroy() {
if (player != null) {
player.release();
player = null;
}
super.onDestroy();
}
}Client Activity thực hiện liên kết:
public class ControlActivity extends AppCompatActivity {
private InteractiveAudioService audioService;
private boolean isBound = false;
private final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
InteractiveAudioService.LocalBinder binder = (InteractiveAudioService.LocalBinder) service;
audioService = binder.getService();
isBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_control);
findViewById(R.id.btn_play).setOnClickListener(v -> {
if (isBound) audioService.play();
});
findViewById(R.id.btn_pause).setOnClickListener(v -> {
if (isBound) audioService.pause();
});
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, InteractiveAudioService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (isBound) {
unbindService(connection);
isBound = false;
}
}
}