技术需求:实现后台定时推送自定义消息(参考Auto-Athkar应用)
Hey there! Let's break down how to implement those two background messaging features you need, matching the behavior of Auto-Athkar for Muslims. Here's a step-by-step solution tailored to Android's background restrictions:
一、核心技术选型
Android 8.0+ has strict background limits, so we'll use WorkManager (Google's recommended tool for scheduled tasks) — it handles background execution even when the app isn't running, and we'll pair it with wake locks to ensure messages show when the device is awake.
二、Step-by-Step Implementation
1. Add Dependencies
First, include WorkManager in your app-level build.gradle (Module):
dependencies { // Core WorkManager library implementation "androidx.work:work-runtime:2.8.1" // Kotlin extensions (optional but recommended for coroutine support) implementation "androidx.work:work-runtime-ktx:2.8.1" }
2. Create a Worker Class (Handles Message Display)
This class will handle showing your custom message (we'll use notifications as an example, like Auto-Athkar) and manage wake locks to keep the device awake temporarily:
import android.app.NotificationManager import android.content.Context import android.os.Build import android.os.PowerManager import androidx.core.app.NotificationCompat import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlin.random.Random class CustomMessageWorker( private val context: Context, params: WorkerParameters ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result = withContext(Dispatchers.IO) { // Acquire wake lock to ensure device stays awake while showing the message val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager val wakeLock = powerManager.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP, "MyApp:MessageWakeLock" ) wakeLock.acquire(10000) // Hold for 10 seconds to ensure visibility try { // Show your custom message (replace with your own UI/notification logic) showCustomNotification() } finally { if (wakeLock.isHeld) wakeLock.release() } // Return success to allow re-scheduling (for random intervals) return@withContext Result.success() } private fun showCustomNotification() { val notificationManager = context.getSystemService(NotificationManager::class.java) // Create notification channel (required for Android 8.0+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = android.app.NotificationChannel( "custom_message_channel", "Custom Reminders", NotificationManager.IMPORTANCE_DEFAULT ).apply { description = "Timed custom messages from your app" } notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(context, "custom_message_channel") .setSmallIcon(R.drawable.ic_your_notification_icon) // Replace with your icon .setContentTitle("Your Custom Title") .setContentText("This is your scheduled custom message!") // Replace with dynamic content .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() // Use random ID to avoid overwriting previous notifications notificationManager.notify(Random.nextInt(), notification) } }
3. Schedule the Two Task Types
(1) Fixed 10-Second Interval Task
Use PeriodicWorkRequest for consistent, repeated execution:
import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.workDataOf import java.util.concurrent.TimeUnit fun scheduleFixedIntervalTask(context: Context) { val fixedWorkRequest = PeriodicWorkRequestBuilder<CustomMessageWorker>(10, TimeUnit.SECONDS) .setInputData(workDataOf("task_type" to "fixed")) .setConstraints(androidx.work.Constraints.Builder() .setRequiresDeviceIdle(false) // Don't wait for device to be idle .setRequiresCharging(false) // Run even if not charging .build()) .build() // Enqueue as a unique task to avoid duplicate schedules WorkManager.getInstance(context).enqueueUniquePeriodicWork( "fixed_interval_task", ExistingPeriodicWorkPolicy.REPLACE, fixedWorkRequest ) }
(2) Random 10-20 Second Interval Task
Since periodic requests can't have random intervals, use OneTimeWorkRequest and re-schedule after each execution:
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import androidx.work.ExistingWorkPolicy import kotlin.random.Random import java.util.concurrent.TimeUnit fun scheduleRandomIntervalTask(context: Context) { // Generate random delay between 10-20 seconds val randomDelay = Random.nextLong(10, 21) val randomWorkRequest = OneTimeWorkRequestBuilder<CustomMessageWorker>() .setInitialDelay(randomDelay, TimeUnit.SECONDS) .setInputData(workDataOf("task_type" to "random")) .setConstraints(androidx.work.Constraints.Builder() .setRequiresDeviceIdle(false) .setRequiresCharging(false) .build()) .build() // Enqueue unique task to replace any existing random task WorkManager.getInstance(context).enqueueUniqueWork( "random_interval_task", ExistingWorkPolicy.REPLACE, randomWorkRequest ) // Re-schedule the task once it completes WorkManager.getInstance(context).getWorkInfoByIdLiveData(randomWorkRequest.id) .observeForever { workInfo -> if (workInfo.state == androidx.work.WorkInfo.State.SUCCEEDED) { scheduleRandomIntervalTask(context) } } }
4. Trigger the Tasks
Add buttons or a settings screen to let users start/stop the tasks (example in an Activity):
import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Start fixed interval task findViewById<android.widget.Button>(R.id.btn_start_fixed).setOnClickListener { scheduleFixedIntervalTask(this) Toast.makeText(this, "Fixed interval task started", Toast.LENGTH_SHORT).show() } // Start random interval task findViewById<android.widget.Button>(R.id.btn_start_random).setOnClickListener { scheduleRandomIntervalTask(this) Toast.makeText(this, "Random interval task started", Toast.LENGTH_SHORT).show() } } }
5. Required Permissions
Add these to your AndroidManifest.xml:
<!-- For wake lock access --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- For notifications (Android 13+) --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
For Android 13+, remember to request the POST_NOTIFICATIONS permission dynamically in your code.
三、Key Notes for Auto-Athkar Like Behavior
- Wake Lock Handling: The wake lock ensures messages show even if the device is awake but not actively used (like Auto-Athkar's prayer reminders).
- Background Resilience: WorkManager automatically handles Doze mode and app standby, so tasks will run when the device is awake.
- Battery Optimization: Add a toggle in your app to let users disable tasks when needed, and consider adding constraints (like only running when charging) to reduce battery usage.
内容的提问来源于stack exchange,提问作者PerfectDroid

