Execute function every full minute android/kotlin - android

Is this possible to execute function in android every minute starting from full minute, so eg. when I run my app at 8:30:12 the first function call will be at 8:31:00 and next at 8:32:00 and so on.

You have to first calculate the time required to become exact minute that is starting the task at 8:30:12, first call should happen at 8:31:00.
So for that you can pull current time using System.currentTimeMillis() and then ceil it's minute value to the nearest integer,
// import kotlin.math.ceil
val firstCallTime = ceil(System.currentTimeMillis() / 60_000.0).toLong() * 60_000
Then you can use coroutines to handle your task that will use a CommonPool to get reusable threads from and won't require to you to create threads.
// scope can be anything, for instance you want to call the function
// only when client is using your application then use viewModelScope provided,
// otherwise your own scope like `val scope = CoroutineScope(Dispatchers.Main)`
// Or use Dispatchers.Default for CPU intensive tasks
val parentJob = scope.launch {
// suspend till first minute comes after some seconds
delay(firstCallTime - System.currentTimeMillis())
while (true) {
launch {
yourFunctionToBeCalled()
}
delay(60_000) // 1 minute delay (suspending)
}
}
// If you ever want your job to cancel just do a cancel,
// the function delay suspends and so cancellable without interrupting the threads
// parentJob.cancel()

Use PeriodicWorkRequest along with WorkManager class in android. It goes like this:
val work = PeriodicWorkRequestBuilder<MyWorker>(60, TimeUnit.SECONDS)
.build();
val workManager = WorkManager.getInstance(context);
workManager.enqueuePeriodicWork(work);

Related

Ongoin use of variable in Worker class Android

Im trying to perform a one time work with the worker class
where the doWork method will just count a number
Call it I.
This will loop through 100 times, and in every iteration, the counter will continue grow up.
I added a constraint for this work to be network Constraining connected.
I would like to count every time and if I close the Network the count will stop.
After I open the network I would like The count to continue work from the same count
value that was there before
Every time I close the network and open it again the count starts from zero again and the value that was in the counter before didn't save in the global variable I that I defined in the worker. it's like not taking the last value that was defined there.
My worker class
class WorkerClass(appContext: Context, workerParams: WorkerParameters):
Worker(appContext, workerParams) {
var i:Int=0
override fun doWork(): Result {
while (i < 100) {
if (this.isStopped){
Log.i("Hello","I Stopped A")
return Result.retry()
}
i++
Log.i("Hello", "The value of counter is :"+i.toString())
Thread.sleep(200)
}
return Result.success()
}
}
My simple work creator: (There is a data passed I don't use it right now so you can ignore it.
val constrints=Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val k = sceondTxt.text.toString()
val data=Data.Builder()
data.putString("Counter Value",k)
val oneReq:WorkRequest= OneTimeWorkRequestBuilder<WorkerClass>().setConstraints(constrints).setInputData(data.build())
.build()
So as you can see I define an I as Integer and every iteration of the work the I is growing. but when I close the connection and open it again the I is 0 again and didn't save from the last run of doWork.
does the definition of i is not global? the system wont take the last value of I that was stored in the doWork for later iterations?
How can I save the value of something in my doWork to later use it again (after the constraints are met) not as the default value?

How to make a guaranteed real-time stopwatch Android

I'm trying to use a foreground service to implement a count up timer feature in my app, essentially a stopwatch. Below is the general idea of what I'm doing, which works well when the app is in the foreground, but not when the screen is off.
This is because handler.postDelayed() is not guaranteed to be real time, and when the screen is off, it's far from it.
class TimerService: Service(), CoroutineScope {
private var currentTime: Int = 0
private val handler = Handler(Looper.getMainLooper())
private var runnable: Runnable = object : Runnable {
override fun run() {
if (timerState == TimerState.START) {
currentTime++
}
broadcastUpdate()
// Repeat every 1 second
handler.postDelayed(this, 1000)
}
}
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + job
// Call this to start the timer
private fun startCoroutineTimer() {
launch(coroutineContext) {
handler.post(runnable)
}
}
// Rest of the service class
...
}
My question is, what is the cannonical way to do something like this? I need to be able to guarantee, using a foreground service, that I can make an accurate stopwatch that is able to start, pause, resume, and stop.
I have the state broadcasting and everything else worked out already, but the actual timer part is where I'm stuck, and I can't seem to find a simple and efficient solution that is guaranteed to be accurate.
First start with a different OS. There's an entire class of OSes called RTOS (real time OS). Linux (and thus Android) are not one. If you actually need realtime, linux is not an acceptable solution.
But let's say you don't actually need realtime, you just want higher accuracy. There's a few ways to easily improve your code.
The biggest thing is that your current code assumes the timer will go off once per second. Don't do that. Instead, keep track of the time when the timer starts. Each time the timer goes off, get the current time. The time elapsed is the delta. That way, any accumulated inaccuracies get wiped away each tick. That will also fix a lot of your screen off case, as the first update after screen on will update to the correct time elapsed.
private var timeStarted : Long = 0
private var timeElapsed : Long = 0
private var runnable: Runnable = object : Runnable {
override fun run() {
if (timerState == TimerState.START) {
timeElapsed = System.getCurrentTimeMillis() - timeStarted
}
broadcastUpdate()
// Repeat every 1 second
handler.postDelayed(this, 1000)
}
}
private fun startCoroutineTimer() {
timeStarted = System.getCurrentTimeMillis()
handler.post(runnable)
}
Also notice you don't need a coroutine to post to a handler. The handler takes care of multithreading, launching a coroutine there provides no value.

Android workmanager minimum interval workaround

I am using the workmanager to run a service in the background, minimum interval from documentation is 15 minutes, but i would like to have an interval of 2 minutes.
I tried this code, it is working while debugging from android studio and runs properly every two minutes, but once i unplug the cable, or deploy it directly via apk, it runs only one time and stops.
In main activity onCreate/onResume :
OneTimeWorkRequest oneTimeRequest = new OneTimeWorkRequest.Builder(CallAndSmsWork.class).addTag(TAG).setInitialDelay(15, TimeUnit.SECONDS).build();
WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork("CallnSms", ExistingWorkPolicy.REPLACE, oneTimeRequest);
In the end of DoWork method of CallAndSmsWork service :
OneTimeWorkRequest oneTimeRequest = new OneTimeWorkRequest.Builder(CallAndSmsWork.class).setInitialDelay(120, TimeUnit.SECONDS).addTag(TAG).build();
WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork("CallnSms", ExistingWorkPolicy.REPLACE, oneTimeRequest);
Is there anything i did wrong, or any other option to have the service running properly every two minutes ?
P.S.: i am on android 10 , one plus 6t
For a 2 min interval you could use a periodic runnable, something like:
private val runnable = object : Runnable {
val periodInMillis = 2 * 60 * 1000L
override fun run() {
Log.d(DEBUG_TAG,"Runnable scheduled")
// Do stuff
mHandler?.postDelayed(this,periodInMillis)
}
}
And send it to a handler thread:
//Threading madness
private var mThread: HandlerThread? = null
private var mHandler: Handler? = null
Than you can initialize:
//Prepare Thread
if (mThread == null) {
mThread = HandlerThread("HeartBeatThread", Process.THREAD_PRIORITY_BACKGROUND)
mThread!!.start()
}
if (mHandler == null) mHandler =
Handler(mThread!!.looper) //Blocks until looper is prepared, which is fairly quick
mHandler!!.post(runnable)
In this way you have a task that enqueues itself and runs every 2 minutes. The issue with that could be Doze mode and something to do with the way that Android handles battery saver mode. For that you can pick up a lock I guess.

Limiting the maximum number of coroutines that can run in a scope

I am translating our current application from Java to Kotlin and I came upon this problem.
The java implementation used to use threads to transfer data from the server.
It would create about 100 different threads that would request data, but from what I have seen no more than 4 would run at a time, the others would wait for a thread to finish before starting.
When translating it to Kotlin I used Coroutines
This creates a problem because apparently the server can't handle 100 requests actually being sent.
All coroutines are launched in the same scope , so it's something like this:
//this is a custom scope that launches on Dispatchers.IO + a job that I can use to cancel everything
transferScope.launch {
//loadData is a suspending function that returns true/false
val futures = mDownloadJobs.map{ async { it.loadData() } }
val responses = futures.awaitAll()
//check that everything in responses is true etc....
}
Is there a way to make the specific transferScope to allow only up to 5 coroutines at a time and then when one finishes let a different one run? (I do not care about the order)
If it can't be done through the scope, is there a different way this can be achieved?
Require each coroutine to acquire a Kotlin Semaphore permit from a total of 5 permits before making a request.
Something like this:
import kotlinx.coroutines.sync.Semaphore
val requestSemaphore = Semaphore(5)
val futures = mDownloadJobs.map {
async {
// Will limit number of concurrent requests to 5
requestSemaphore.withPermit {
it.loadData()
}
}
}
val responses = futures.awaitAll()
You can do something like this, group the requests into chunks of 4, launch coroutines to process them and wait till that group is finished before launching a new one.
requests.chunked(4).forEachIndexed { index, chunk ->
coroutineScope {
LOG("processing chunk $index")
chunk.forEach {
launch {
delay(100)
}
}
LOG("done processing $index")
}
}
I believe you should Channel and limit the creation of coroutine that you are creating.
val channel = Channel<Job>()
transferScope.launch {
mDownloadJobs.forEach { channel.send(it) }
channel.close() // don't forget to close the channel
}
coroutineScope {
val responses = mutableListOf<Any>()
repeat(5).map {
launch {
for (job in mDownloadJobsChannel) {
responses.add(jobs.loadData())
}
}
}
}
Parallelization in this case is 5 coroutines.
I did not test this code :D and I am sure there are cleaner ways to do this.
Dispatchers.IO claims to create a thread pool and restrict span out to that pool. It's docstring tells you how to change the pool size (a system property tho).

setOverrideDeadline() doesn't seem to have an effect

I've got an app widget where I want to maintain up-to-date information in a line of text that says "x minutes ago", referring to the age of the bitmap below it. I wrote the code as follows:
private fun scheduleUpdateAge(context: Context) {
val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val resultCode = jobScheduler.schedule(
JobInfo.Builder(UPDATE_AGE_JOB_ID, ComponentName(context, UpdateAgeService::class.java))
.setMinimumLatency(MINUTE_IN_MILLIS)
.setOverrideDeadline(2 * MINUTE_IN_MILLIS)
.build())
reportScheduleResult("update age", MINUTE_IN_MILLIS, resultCode)
}
In the last line i check the result of scheduling, it's always successful.
In the job service I update the remote views and then reschedule the job:
class UpdateAgeService : JobService() {
override fun onStartJob(params: JobParameters): Boolean {
MyLog.i("UpdateAgeService start job")
updateRemoteViews(applicationContext)
jobFinished(params, false)
scheduleUpdateAge(applicationContext)
return false
}
override fun onStopJob(params: JobParameters): Boolean {
MyLog.i("UpdateAgeService stop job")
return true
}
}
However, I keep observing that Android has not invoked my job for much longer periods, above ten minutes. At that time the other scheduled job, the one that refreshes the bitmap, kicks in and resets the age text as well.
The documentation on setOverrideDeadline states
Set deadline which is the maximum scheduling latency. The job will be run by this deadline even if other requirements are not met.
but Android seems to violate it.
Am I missing something here?
I'm aware that the "natural" way to schedule periodic jobs is setPeriodic(), but setPeriodic() wasn't invoking my job either and its contracts clearly states
You have no control over when within this interval this job will be executed, only the guarantee that it will be executed at most once within this interval.
So missing the schedule is "by the book" for that case.

Categories

Resources