Giới thiệu
Ví dụ này minh họa cách gửi thông báo khi người dùng đóng hoàn toàn ứng dụng (không chỉ ẩn vào background). Sử dụng Service kết hợp với Timer để tiếp tục chạy nền và hiển thị thông báo định kỳ.
Kiến trúc giải pháp
- MainActivity: Giao diện chính, khởi động Service khi
onStop()được gọi - NotificationService: Service chạy nền, dùng Timer để tạo thông báo định kỳ
Các bước thực hiện
Bước 1: Tạo dự án mới
Tạo project Android Studio mới.
Bước 2: Layout chính (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:onClick="closeApp"
android:text="Đóng ứng dụng để nhận thông báo" />
</RelativeLayout>
Bước 3: MainActivity (src/MainActivity.java)
package app.tutorialspoint.com.notifyme;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStop() {
super.onStop();
// QUAN TRỌNG: Khởi động Service khi app đóng
startService(new Intent(this, NotificationService.class));
}
public void closeApp(View view) {
finish(); // Kết thúc Activity hoàn toàn
}
}
Bước 4: NotificationService (src/NotificationService.java)
package app.tutorialspoint.com.notifyme;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
public class NotificationService extends Service {
public static final String NOTIFICATION_CHANNEL_ID = "10001";
private final static String default_notification_channel_id = "default";
Timer timer;
TimerTask timerTask;
String TAG = "Timers";
int YOUR_X_SECS = 5; // Demo: 5 giây. Thực tế: 300 (5 phút)
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY; // Restart service nếu bị kill
}
@Override
public void onCreate() {
Log.e(TAG, "onCreate");
}
@Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
stopTimerTask();
super.onDestroy();
}
final Handler handler = new Handler();
public void startTimer() {
timer = new Timer();
initializeTimerTask();
// Chạy lần đầu sau 5 giây, sau đó mỗi 5 giây
timer.schedule(timerTask, 5000, YOUR_X_SECS * 1000);
}
public void stopTimerTask() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
handler.post(new Runnable() {
public void run() {
createNotification();
}
});
}
};
}
private void createNotification() {
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext(), default_notification_channel_id);
mBuilder.setContentTitle("App đã đóng");
mBuilder.setContentText("Thông báo từ Service chạy nền");
mBuilder.setTicker("Thông báo background");
mBuilder.setSmallIcon(R.drawable.ic_launcher_foreground);
mBuilder.setAutoCancel(true);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel notificationChannel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "NOTIFICATION_CHANNEL_NAME", importance);
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
assert mNotificationManager != null;
mNotificationManager.createNotificationChannel(notificationChannel);
}
assert mNotificationManager != null;
mNotificationManager.notify((int) System.currentTimeMillis(), mBuilder.build());
}
}
Bước 5: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.tutorialspoint.com.notifyme">
<uses-permission android:name="android.permission.VIBRATE" />
<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=".NotificationService"
android:label="@string/app_name">
<intent-filter>
<action android:name="app.tutorialspoint.com.notifyme.NotificationService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>
Lưu ý quan trọng cho Production
⚠️ Hạn chế trên Android 8.0+ (API 26+)
- Background Service bị giới hạn thời gian chạy
- Nên dùng WorkManager cho tác vụ định kỳ tin cậy
- Hoặc Foreground Service với notification luôn hiển thị
- Cần quyền
FOREGROUND_SERVICEtrong Manifest
Chạy thử nghiệm
Cài đặt app, mở lên, nhấn nút "Đóng ứng dụng để nhận thông báo". Đợi vài giây để thấy thông báo xuất hiện trên màn hình khóa hoặc thanh thông báo.