最新Android版本实现可抗任务管理器清理的后台持续运行服务方案
Hey, let's break down how to build this properly—since you've already tried AlarmManager and found it only restarts the service (not keeps it alive through swipes), we'll focus on Oreo-compliant, non-hacky methods that prioritize no notifications where possible.
Core Approach: JobScheduler + Bound Service
Android Oreo's background restrictions make regular Service instances easy targets for system cleanup. JobScheduler is the official replacement, and pairing it with a bound service ensures your process stays prioritized (since bound services are tied to active connections, which the system avoids killing).
Step 1: Implement the JobService
This job will act as a watchdog—when it triggers, it binds to your persistent service to keep the process alive, and reschedules itself if it gets stopped.
class PersistentJobService : JobService() { private var serviceConnection: ServiceConnection? = null override fun onStartJob(params: JobParameters?): Boolean { // Bind to our persistent service to maintain an active connection serviceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { // Connection established—keep it alive } override fun onServiceDisconnected(name: ComponentName?) { // Re-bind if connection drops bindService( Intent(this@PersistentJobService, PersistentService::class.java), this, BIND_AUTO_CREATE ) } } bindService( Intent(this, PersistentService::class.java), serviceConnection!!, BIND_AUTO_CREATE ) return true // Indicates job is still running } override fun onStopJob(params: JobParameters?): Boolean { // Cleanup and re-schedule the job to maintain persistence serviceConnection?.let { unbindService(it) } scheduleJob() return true // Request re-scheduling if job was terminated } private fun scheduleJob() { val jobInfo = JobInfo.Builder(1001, ComponentName(this, PersistentJobService::class.java)) .setPersisted(true) // Retain job after device reboot .setMinimumLatency(1000) // Trigger 1 second after scheduling .setOverrideDeadline(2000) // Ensure job runs within 2 seconds .setRequiresBatteryNotLow(false) // Don't restrict to high battery .setRequiresCharging(false) .build() (getSystemService(JOB_SCHEDULER_SERVICE) as JobScheduler).schedule(jobInfo) } }
Step 2: Build the Bound Persistent Service
This service will handle your actual background logic. Since it's bound to the JobService, we can avoid showing a noticeable foreground notification (or use a nearly invisible one if system rules require it).
class PersistentService : Service() { private val binder = LocalBinder() inner class LocalBinder : Binder() { fun getService(): PersistentService = this@PersistentService } override fun onBind(intent: Intent?): IBinder { // Elevate to foreground priority with a minimal, unobtrusive notification startForeground(1002, createLowProfileNotification()) return binder } private fun createLowProfileNotification(): Notification { val channelId = "persistent_service_channel" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Create a low-priority channel hidden from the user's notification shade val channel = NotificationChannel( channelId, "Persistent Service", NotificationManager.IMPORTANCE_MIN ).apply { setShowBadge(false) lockscreenVisibility = Notification.VISIBILITY_SECRET } getSystemService(NotificationManager::class.java).createNotificationChannel(channel) } // Build a nearly invisible notification that meets system requirements return NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_transparent) // Use a 1x1 transparent icon .setContentTitle("") .setContentText("") .setPriority(NotificationCompat.PRIORITY_MIN) .build() } override fun onDestroy() { super.onDestroy() stopForeground(STOP_FOREGROUND_REMOVE) } }
Key Optimizations to Survive Swipe Cleanup
- Foreground Priority: Even with a minimal notification,
startForegroundpushes your process to the foreground priority tier, making it much harder for system swipe cleanup to kill it. - Auto-Rebind: If the connection between
JobServiceandPersistentServicedrops, we immediately re-bind to maintain the active connection that keeps the process alive. - Reboot Resilience: Add the
RECEIVE_BOOT_COMPLETEDpermission to your manifest, and create a broadcast receiver to re-schedule the job when the device boots:<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <receiver android:name=".BootCompletedReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>class BootCompletedReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == Intent.ACTION_BOOT_COMPLETED) { context?.let { val jobInfo = JobInfo.Builder(1001, ComponentName(it, PersistentJobService::class.java)) .setPersisted(true) .setMinimumLatency(1000) .build() (it.getSystemService(JOB_SCHEDULER_SERVICE) as JobScheduler).schedule(jobInfo) } } } }
Critical Notes for Custom ROMs
- Battery Optimization: Most OEMs (Xiaomi, Huawei, Samsung) have aggressive battery savers. You'll need to guide users to add your app to the "No Optimization" list in device settings—without this, even the best implementation will get killed.
- Swipe Cleanup Behavior: On some devices, swiping an app kills the entire process. In this case,
JobSchedulerwill automatically re-schedule the job within a few minutes (system-dependent) to restart your service.
内容的提问来源于stack exchange,提问作者user9367441

