2

In my case , I use std::this_thread::sleep_for(10ms) to sleep 10ms.

If the Android app is in foreground, will sleep about 10ms. But if app in background ,it will sleep about 50ms~.

I also tried usleep(),nanosleep(),std::condition::wait_for(), and also java Thread.sleep(), NONE of them works fine in this case.

But this code works fine always:

int64_t startTimeStemp = now_ms();
while(true) {
  int64_t nowTimeStemp = now_ms();
  if(now - start > 10) {
     break;
  }
}

How can I solve this problem? Thanks.

9
  • I don't think you can get guaranteed just 'at least'. Related at least conceptually: Investigation of optimal sleep time calculation in game loop Commented Jan 25, 2022 at 4:10
  • I'll try scheduleAtFixedRate, and then post the result.Thanks @MorrisonChang Commented Jan 25, 2022 at 7:38
  • scheduleAtFixedRate is still not accurate when in background , maybe 10ms ~45ms. Orz Commented Jan 25, 2022 at 15:07
  • You should probably explain what you are trying to do. Realize that Android isn't a RTOS so background processes have few guarantees (and power management/battery life requirements means that should be expected). Commented Jan 25, 2022 at 19:06
  • I am coding a media player , when audio and video syncs , it needs to sleep in the accurate time , or at least without a large difference. @MorrisonChang Commented Jan 26, 2022 at 11:08

1 Answer 1

0

So there is a solution. Its called foreground notification service. First I was doing wake lock but this is insufficient:

Now my code looks like this (sorry I use a lot of custom extensions)

    app.startService<LooperPlayNotificationService>()
    wakeLock.acquire()

So I keep app alive and working fine in background.

class LooperPlayNotificationService : Service() {

    companion object {
        val NOTIFICATIONS_CHANNEL = "${app.packageName} notifications"
    }

    override fun onBind(intent: Intent): IBinder? = null

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)
        start()
        return START_STICKY
    }

    override fun onCreate() {
        super.onCreate()
        start()
    }

    private val playButtonActionId = "play_button_action"
    private lateinit var playButtonAction: BroadcastReceiver
    private var started = false

    // https://stackoverflow.com/questions/6619143/start-sticky-foreground-android-service-goes-away-without-notice
    // There's a bug in 2.3 (not sure if it was fixed yet) where when a Service is killed and restarted,
    // its onStartCommand() will NOT be called again. Instead you're going to have to do any setting up in onCreate()
    private fun start() {
        if (started) return
        started = true
        startForeground(647823876, createNotification())
        playButtonAction = register(playButtonActionId) {
            main.looper?.player?.asStarted { it.stop() }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        unregister(this.playButtonAction)
    }

    private fun createNotification() = Builder(this, NOTIFICATIONS_CHANNEL)
        .setSmallIcon(outline_all_inclusive_24)
        .setContentIntent(getActivity(this, 0, Intent<InstrumentsActivity>(this),
            FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE))
        .setPriority(PRIORITY_DEFAULT)
        .setAutoCancel(false).setOngoing(true)
        .addAction(ic_stop_circle_black_24dp, "Stop",
            getBroadcast(this, 0, Intent(playButtonActionId),
                FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE))
        .setContentText(getString(R.string.app_name))
        .setContentText(main.looper?.preset?.item?.value?.title?.value).build()
}

this is basically just a service, it has to be defined in manifest:

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

    <service
        android:name=".model.mode.looper.player.state.LooperPlayNotificationService"
        android:enabled="true"
        android:exported="true" />

And so on, there is bunch of examples about this matter, but overall it was not so trivial to implement due to various details you can see in code I posted.

Not the answer you're looking for? Browse other questions tagged or ask your own question.