You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在onStop()/onDestroy()中显示弹窗、自定义Toast及实现定时通知?

Hey there! Let's walk through each of your Android app requirements with practical, actionable code and tips—no fluff, just what you need to implement this properly.


1. Trigger Notifications at a Specific Time

For scheduled notifications, you have two reliable options depending on your needs:

Option A: AlarmManager (For Precise, Time-Critical Triggers)

Great if you need exact timing, but note that on Android 12+, you'll need to handle restrictions for background alarms. Here's how to set it up:

First, create a notification channel (required for Android 8.0+):

private fun createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = "Scheduled Alerts"
        val descriptionText = "Notifications for scheduled events"
        val importance = NotificationManager.IMPORTANCE_DEFAULT
        val channel = NotificationChannel("SCHEDULED_CHANNEL_ID", name, importance).apply {
            description = descriptionText
        }
        val notificationManager: NotificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel)
    }
}

Next, set up the AlarmManager to trigger a BroadcastReceiver at your target time:

fun scheduleNotification(targetTime: Long) {
    val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
    val intent = Intent(this, NotificationReceiver::class.java)
    val pendingIntent = PendingIntent.getBroadcast(
        this,
        0,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE // Use IMMUTABLE for API 31+
    )

    // Set the alarm (use RTC_WAKEUP to wake the device if needed)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(
            AlarmManager.RTC_WAKEUP,
            targetTime,
            pendingIntent
        )
    } else {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, targetTime, pendingIntent)
    }
}

Then create the BroadcastReceiver to fire the notification:

class NotificationReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        context ?: return
        val notificationBuilder = NotificationCompat.Builder(context, "SCHEDULED_CHANNEL_ID")
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle("Scheduled Alert")
            .setContentText("Your scheduled time has arrived!")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setAutoCancel(true)

        with(NotificationManagerCompat.from(context)) {
            notify(1, notificationBuilder.build())
        }
    }
}

Option B: WorkManager (For Non-Critical, Resilient Scheduling)

Perfect if you need the notification to fire even if the app is killed or the device restarts. Use OneTimeWorkRequest with a delay:

fun scheduleWorkManagerNotification(delayMillis: Long) {
    val workRequest = OneTimeWorkRequestBuilder<NotificationWorker>()
        .setInitialDelay(delayMillis, TimeUnit.MILLISECONDS)
        .build()
    WorkManager.getInstance(this).enqueue(workRequest)
}

class NotificationWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
    override fun doWork(): Result {
        // Same notification building code as above
        val notificationBuilder = NotificationCompat.Builder(applicationContext, "SCHEDULED_CHANNEL_ID")
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle("WorkManager Alert")
            .setContentText("Your scheduled time has arrived!")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setAutoCancel(true)

        with(NotificationManagerCompat.from(applicationContext)) {
            notify(2, notificationBuilder.build())
        }
        return Result.success()
    }
}

2. Show Popup/Custom Layout in onStop() or onDestroy()

First, a critical note: onDestroy() is not guaranteed to be called (Android may kill your app process without invoking it). So focus on onStop() for reliable behavior.

To show a popup or custom overlay when the app moves to the background, you'll need to use a system overlay window (since your Activity is no longer in the foreground). Here's how:

Step 1: Request Required Permissions

Add this to your AndroidManifest.xml:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

For Android 6.0+, you need to request the permission dynamically (users have to enable it in settings):

private fun checkOverlayPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (!Settings.canDrawOverlays(this)) {
            val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))
            startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE)
        }
    }
}

Step 2: Show Custom Overlay in onStop()

Create a custom layout (e.g., layout/custom_overlay.xml) and add it to the WindowManager:

override fun onStop() {
    super.onStop()
    if (Settings.canDrawOverlays(this)) {
        showCustomOverlay()
    }
}

private fun showCustomOverlay() {
    val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
    val overlayView = LayoutInflater.from(this).inflate(R.layout.custom_overlay, null)

    // Configure window parameters
    val params = WindowManager.LayoutParams(
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.WRAP_CONTENT,
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
        } else {
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
        },
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
        PixelFormat.TRANSLUCENT
    )
    params.gravity = Gravity.CENTER

    // Add the view to the window
    windowManager.addView(overlayView, params)

    // Optional: Add a close button to the overlay
    overlayView.findViewById<Button>(R.id.btn_close).setOnClickListener {
        windowManager.removeView(overlayView)
    }
}

⚠️ Important: Avoid adding overlays in onDestroy()—by the time this method is called, your app's resources may already be released, leading to crashes or leaks.


3. Show Custom Toast in onStop() or onDestroy()

Custom Toasts work here, but you must use the application context instead of the Activity context to avoid memory leaks (since the Activity is being destroyed). Here's how:

Step 1: Create Custom Toast Layout

Make a layout file (e.g., layout/custom_toast.xml) with your desired design.

Step 2: Display the Custom Toast

override fun onStop() {
    super.onStop()
    showCustomToast()
}

private fun showCustomToast() {
    val toast = Toast(applicationContext)
    val toastView = LayoutInflater.from(applicationContext).inflate(R.layout.custom_toast, null)
    toastView.findViewById<TextView>(R.id.toast_text).text = "App is moving to background!"
    toast.view = toastView
    toast.duration = Toast.LENGTH_LONG
    toast.show()
}

⚠️ Note: Toasts shown in onDestroy() may not appear if the app process is immediately killed by the system. Stick to onStop() for consistent results.


内容的提问来源于stack exchange,提问作者Maamoun Med Oussama

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:05:54