Foreground Service là gì?
Foreground Service là Service chạy nền nhưng có hiển thị Notification cố định, cho phép hệ thống ưu tiên không kill tiến trình. Bắt buộc đối với các tác vụ dài như phát nhạc, tải file, tracking GPS từ Android 8.0+.
Triển khai startForeground()
Bước 1: Tạo dự án
Android Studio → New Project → Empty Activity.
Bước 2: Layout (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Foreground Service"
android:textSize="25sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Bước 3: MainActivity.java
package com.example.andy.myapplication;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView text = findViewById(R.id.text);
text.setOnClickListener(v -> {
if (isMyServiceRunning(ForegroundService.class)) {
text.setText("Stopped");
stopService(new Intent(this, ForegroundService.class));
} else {
text.setText("Started");
startService(new Intent(this, ForegroundService.class));
}
});
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
}
Bước 4: ForegroundService.java - Core logic
package com.example.andy.myapplication;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.IBinder;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import android.widget.Toast;
public class ForegroundService extends Service {
private static final String CHANNEL_ID = "ForegroundServiceChannel";
private static final int NOTIFICATION_ID = 1337;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("My Awesome App")
.setContentText("Foreground Service đang chạy...")
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_LOW)
.build();
// Bắt đầu Foreground Service
startForeground(NOTIFICATION_ID, notification);
Toast.makeText(this, "Foreground Service started", Toast.LENGTH_LONG).show();
return START_STICKY;
}
@RequiresApi(api = Build.VERSION_CODES.O)
private void createNotificationChannel() {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"My Background Service",
NotificationManager.IMPORTANCE_LOW
);
channel.setLightColor(Color.BLUE);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (manager != null) {
manager.createNotificationChannel(channel);
}
}
@Override
public void onDestroy() {
super.onDestroy();
stopForeground(true); // true = xóa notification
Toast.makeText(this, "Foreground Service destroyed", Toast.LENGTH_LONG).show();
}
}
Bước 5: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.andy.myapplication">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".ForegroundService"
android:foregroundServiceType="mediaPlayback" />
</application>
</manifest>
Giải thích quan trọng
- Notification Channel: Bắt buộc từ Android O (API 26)
- startForeground(id, notification): Đưa service lên foreground
- stopForeground(true): Dừng foreground và xóa notification
- android:foregroundServiceType: Bắt buộc từ Android 10 (API 29), ví dụ:
mediaPlayback,location,dataSync... - PendingIntent.FLAG_IMMUTABLE: Bắt buộc từ Android 12 (API 31)
Kết quả
Khi bật service, Notification xuất hiện trên thanh trạng thái và không thể swipe xóa. Service tiếp tục chạy ngay khi app bị tắt.