【问题标题】:How to keep service alive even after activity destroyed?即使在活动被破坏后如何​​保持服务活着?
【发布时间】:2016-12-06 09:33:28
【问题描述】:

我在这里运行音乐播放服务。 这段代码 sn-p 在我的 Activity 的 onStart() 方法中

if(musicServiceStartIntent == null) {
            musicServiceStartIntent = new Intent(this, MusicService.class);
            startService(musicServiceStartIntent);
            bindService(musicServiceStartIntent, musicConnection, Context.BIND_AUTO_CREATE);
        } 

首先我启动我的服务然后绑定它。我在 onDestroy() 方法中调用 unbindservice()。我的活动被破坏并且服务停止了。

unbindService(musicConnection);

清单文件声明

<service android:name=".Services.MusicService"/>

即使在活动被破坏后,我如何才能让我的服务在后台运行。我参考了 StackOverflow 的几个线程,但它们没有帮助。

【问题讨论】:

  • stackoverflow.com/questions/22895872/… 试试这个。这对我有用
  • 不要在 Ondestroy() 上解绑服务,然后再销毁,然后让服务变得有粘性。
  • 我试过你的答案。如果我不取消绑定我的服务 onDestroy(),则会出错。
  • @JayGhosh:看看我的代码 sn-p。我按照你的建议写了。但不适合我.... :(
  • 绑定服务的目的是什么?

标签: android android-service android-service-binding


【解决方案1】:

return service.START_STICKY 或 service.START_REDELIVER_INTENT 在onStartCommand

【讨论】:

【解决方案2】:

请参阅https://developer.android.com/guide/components/services.html#Foreground。 播放来自服务的音乐的音乐播放器应设置为在前台运行,因为用户明确知道其操作。状态栏中的通知可能会指示当前歌曲并允许用户启动活动以与音乐播放器进行交互。 要请求您的服务在前台运行,请致电startForeground()。

【讨论】:

    【解决方案3】:

    你只需要启动服务,不要将它绑定到活动生命周期

    Intent intent = new Intent(context, SomeService.class);
    startService(intent);
    

    并且您的服务可以使用 START_STICKY / START_REDELIVER_INTENT 确保在 android 系统终止您的服务时重新创建您的服务

    @Override
    public int onStartCommand(final Intent intent, int flags, int startId) {
       //other code
       return START_STICKY;
    }
    

    如果需要,您可以使用 Service.startForeground(notificationId, notification) 确保您的服务不会被系统杀死

    【讨论】:

    • 你提到 startForeground 了吗?
    • 我已经发布了完全相同的答案,请仔细查看
    • 我已经发布了所有可能的答案,再看一遍
    • 用一个简单的例子
    【解决方案4】:

    在 startForeground 中使用你的服务,使用 Notification 你可以让你的服务保持活跃..

    【讨论】:

    • 它有帮助..您的回答并不总是有帮助。我想在我的活动运行时和销毁后连续运行此服务。
    • 你必须在前台启动你的服务
    • 如何在前台启动服务?我正在调用这个 startService(intent);
    • 您的建议对我有用。请编辑您的答案,我会将其标记为正确答案。谢谢..:D
    • 在具有大量 RAM 的 Android 设备上,您不需要将服务置于前台,但它会使其更具弹性。如果服务正在使用意图,registerReciever 在单独的 ServiceHandler 中! ServiceHandler 将防止在您的 Activity 被用户/系统销毁后接收到意图时使您的服务崩溃。此类服务的示例:github.com/mauron85/cordova-plugin-background-geolocation/blob/…
    【解决方案5】:

    有三个重要的技巧:

    1. 调用 startForegroundService,它会创建一个不限于绑定上下文的长时间运行的服务,并承诺稍后调用 startForeground。
    2. 在 onStartComand 中返回 START_STICKY
    3. 使用 (1) 中承诺的通知调用 startForeground。

    例如,如果你想运行一个 TimerService,在你的 TimerActivity 中你会这样做:

    private var timerService: TimerService? = null
    
    private val timerServiceConnection = object : ServiceConnection {
    
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            val binder = service as TimerService.Binder
            timerService = binder.getService()
        }
    
        override fun onServiceDisconnected(arg0: ComponentName) {
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        startButton.setOnClickListener {
            timerService?.startTimer(60L, 0L)
        }
    }
    
    override fun onStart() {
        super.onStart()
    
        Intent(this, TimerService::class.java).also {
            ContextCompat.startForegroundService(this, it) // that's the first trick
            bindService(it, timerServiceConnection, Context.BIND_AUTO_CREATE)
        }
    }
    

    您的 TimerService 将是这样的:

    class TimerService : Service() {
    
        private val binder = Binder()
    
        private var serviceLooper: Looper? = null
    
        private var serviceHandler: ServiceHandler? = null
    
        private var timer: CountDownTimer? = null
    
        private val notificationUtil by lazy {
            NotificationUtil(this)
        }
    
        override fun onCreate() {
            HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
                start()
                serviceLooper = looper
                serviceHandler = ServiceHandler(looper)
            }
        }
    
        override fun onBind(intent: Intent?): IBinder? = binder
    
        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            val timerRemaining = intent?.getLongExtra(EXTRA_REMAINING, 0) ?: 0L
            if (timerRemaining != 0L) {
                serviceHandler?.obtainMessage()?.also { msg ->
                    msg.arg1 = startId
                    msg.data.putLong(EXTRA_REMAINING, timerRemaining)
                    serviceHandler?.sendMessage(msg)
                }
            }
    
            return START_STICKY // that's the second trick
        }
    
        override fun onDestroy() {
            super.onDestroy()
            timer?.cancel()
        }
    
        fun startTimer(secondsRemaining: Long, id: Long) {
            Intent(this, TimerService::class.java).apply {
                putExtra(EXTRA_REMAINING, secondsRemaining)
            }.also {
                onStartCommand(it, 0, id.toInt())
            }
        }
    
        fun stopTimer() {
            timer?.cancel()
        }
    
        fun updateNotification(secondsRemaining: Long){
            val notification = NotificationCompat.Builder(this, NotificationUtil.CHANNEL_ID_TIMER)
                    .setSmallIcon(R.drawable.ic_timer)
                    .setAutoCancel(true)
                    .setDefaults(0)
                    .setContentTitle(secondsRemaining.formatSeconds())
                    .setContentText("Timer")
                    .setContentIntent(notificationUtil.getPendingIntentWithStack(this, TimerActivity::class.java))
                    .setOngoing(true)
                    .build()
            startForeground(NotificationUtil.NOTIFICATION_ID, notification) // that's the last trick
        }
    
        private fun sendMessage(remaining: Long) {
            Intent(TimerService::class.java.simpleName).apply {
                putExtra(EXTRA_REMAINING, remaining)
            }.also {
                LocalBroadcastManager.getInstance(this).sendBroadcast(it)
            }
        }
    
        private inner class ServiceHandler(looper: Looper) : Handler(looper) {
    
            override fun handleMessage(msg: Message) {
                val secondsRemaining = msg.data.getLong(EXTRA_REMAINING)
                notificationUtil.showTimerStarted(secondsRemaining)
    
                timer = object : CountDownTimer(secondsRemaining * 1000, 1000) {
    
                    override fun onTick(millisUntilFinished: Long) {
                        Log.i(this::class.java.simpleName, "tick ${(millisUntilFinished / 1000L).formatSeconds()}")
                        updateNotification(millisUntilFinished / 1000)
                        sendMessage(millisUntilFinished / 1000)
                    }
    
                    override fun onFinish() {
                        Log.i(this::class.java.simpleName, "finish")
                        notificationUtil.showTimerEnded()
                        sendMessage(0)
                        stopSelf()
                    }
                }.start()
            }
        }
    
        inner class Binder : android.os.Binder() {
            // Return this instance of LocalService so clients can call public methods
            fun getService(): TimerService = this@TimerService
        }
    
        companion object {
    
            const val EXTRA_REMAINING = "EXTRA_REMAINING"
            const val NOTIFICATION_ID = 1 // cannot be 0
    
            fun Long.formatSeconds(): String {
                val s = this % 60
                val m = this / 60 % 60
                val h = this / (60 * 60) % 24
                return if (h > 0) String.format("%d:%02d:%02d", h, m, s)
                else String.format("%02d:%02d", m, s)
            }
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多