observing PeriodicWorkRequest doesn't work - kotlin

I'm wanna to use Worker for creating notifications for Jetpack Compose App.
this a function in TasksViewModel.kt
private fun setPeriodicNotification(context: Context, tasks: Flow<List<Task>>) {
if (notificationGo.value) {
Log.e("asd", "sadsad")
createNotification(context, tasks)
}
val workManager = WorkManager.getInstance(context)
val notificationWorker =
PeriodicWorkRequestBuilder<NotificationWorker>(10, TimeUnit.SECONDS)
.addTag("TAG")
.setInitialDelay(10, TimeUnit.SECONDS)
.build()
workManager.enqueueUniquePeriodicWork("TAG", ExistingPeriodicWorkPolicy.KEEP, notificationWorker)
workManager.getWorkInfoByIdLiveData(notificationWorker.id).observeForever(androidx.lifecycle.Observer {
if(it.state == WorkInfo.State.SUCCEEDED){
Log.e("workinfo", "succeeded")
}
})
}
in the Logcat showing that Worker result - SUCCESS
Worker result SUCCESS for Work [ id=f44d3ebb-74cb-41c0-b1dd-ca25a7c50d94, tags={ com.example.goneappforandroid.NotificationWorker } ]
but after that the log.e is not printed

Related

Launch a new coroutine task when the existing one gets cancelled in kotlin

I would like to recreate a new coroutine for the task if the existing one gets cancelled because of an exception or other reasons. Currently, the only way I can detect the cancellation is via the invokeOnCompletion handler and I need to trigger new job creation for the cancelled task somehow, also invokeOnCompletion is not a suspend function so I have to do some trick there.
It's my current workaround
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
lateinit var backGroundTask: Job
suspend fun main(): Unit = supervisorScope {
val restartChannel = Channel<Boolean>()
val restartBackGroundTaskListener = startListeningForRestartEvent(restartChannel)
backGroundTask = someBackGroundTask()
backGroundTask.invokeOnCompletion {
launch { restartChannel.send(true) }
}
val restartBackGroundTask = restartBackGroundTaskIntermittently()
delay(1000)
restartBackGroundTask.join()
restartBackGroundTaskListener.join()
}
fun CoroutineScope.startListeningForRestartEvent(restartEvents: Channel<Boolean>): Job =
launch {
for (event in restartEvents) {
when (event) {
true -> {
println("newJob created ..")
backGroundTask = someBackGroundTask()
backGroundTask.invokeOnCompletion {
launch { restartEvents.send(true) }
}
}
false -> {}
}
}
}
fun CoroutineScope.someBackGroundTask(): Job =
launch {
repeat(1000) {
delay(400)
println(it)
}
}
// Just to simulate the cancellation due to some reason
fun CoroutineScope.restartBackGroundTaskIntermittently(): Job =
launch {
repeat(1000) {
delay(2000)
backGroundTask.cancelAndJoin()
}
}
I already have an event loop in my code so I just wanna use that to trigger new job creation. startListeningForRestartEvent will continuously listen for a restart event and create a new someBackGroundTask when it receives true.
Questions:
Is there any better way to achieve this?
Is it ok to do launch { restartChannel.send(true) } inside invokeOnCompletion?
Will the NonCancellable coroutine help me here?

How do I get ktor testApplication client to not wait for a coroutine job in the call to complete?

In the ktor application I have a route that starts a session for the created resource that launches a job to manage interactions with that session and auto-terminates the session if there are no interactions for a while
// routing
post("/sessions") {
val sessionName = call.receive<NewSessionRequest>().name
val session = Sessions.newSession(sessionName) // Companion object that creates a session
launch {
var sessionExists = true
while (sessionExists) {
sessionExists = withTimeoutOrNull(session.timeToLive) {
session.lifeChannel.receive()
} ?: false
}
session.close()
Sessions.remove(session)
}
call.respond(HttpStatusCode.Created, sessionName)
}
post("/sessions/{name}") {
// other call that pings the sessions lifeChannel
}
and then some other route that triggers a message to be sent to the session's lifeChannel to keep the session alive.
In normal operation this works quite well and the session is kept alive as desired. However, during testing the test waits until the entire launched job completes before continuing.
#Test
fun `user can create room`() = testApplication {
val response = jsonClient.post("/sessions") {
contentTupe(ContentType.Application.Json)
setBody(NewSessionRequest("sess"))
}
assertEquals(HttpStatusCodes.created, response.status)
}
Will wait until the job completes and the session terminates before completing the test.
How can I get the testApplication to ignore or work outside of the coroutine context of the application as a normal http call would do with a real ktor app? Is this a bad practice as testing for it is non-intuitive?
Edit
Adding a simple test I want to have pass
class CoroutineRoutesTest {
#Test
fun `how to deal with launched coroutine`() {
testApplication {
application {
routing {
get("/launch-job") {
val delay = 1.seconds
launch {
delay(delay)
}
call.respond("Job lasting for $delay")
}
}
}
val response: HttpResponse
val timing = measureTimeMillis {
response = client.get("/launch-job")
}
assertEquals(HttpStatusCode.OK, response.status)
LoggerFactory.getLogger("CoroutineTest").info("response time: $timing ms")
assertTrue(timing < 800)
}
}
}
I can make your last test pass by launching a job from a different coroutine scope. I'm not sure whether this is the desired behavior or not.
#Test
fun `how to deal with launched coroutine`() {
val scope = CoroutineScope(Dispatchers.Default)
testApplication {
application {
routing {
get("/launch-job") {
val delay = 1.seconds
scope.launch {
delay(delay)
}
call.respond("Job lasting for $delay")
}
}
}
// ...
}
}

RxJava: Issue with withLatestFrom operator when using in combination with .subScribeOn

Please find the below kotlin code snippet:
val dummyApi: PublishSubject<String> = PublishSubject.create()
fun withLatestFromtest() {
dummyApi.onNext("1")
getOtherObservable().withLatestFrom(dummyApi)
.subscribeOn(Schedulers.io())
.subscribeBy(
onNext = {
Log.e("TAG", "Pair ${it.first} /// ${it.second}")
},
onError = {
it.printStackTrace()
},
onComplete = {
Log.e("TAG", "Pair2 Complete")
}
)
dummyApi.onNext("2")
dummyApi.onNext("3")
}
private fun getOtherObservable(): Observable<String>{
return Observable.just("API")
.delay(2, TimeUnit.SECONDS)
}
Expected Output -
Pair API /// 3
Pair2 Complete
Actual Output -
Pair2 Complete
When I removed .subscribeOn(Schedulers.io()) it worked as expected. I am not able to understand why scheduler is creating the issue.

What is a clean way to wait for a response?

I am sending a message(custom protocol, no HTTP) to my server and want to wait for a response. It is working with the following code:
class Connection {
val messages: Observable<Message>
fun sendMessageWithAnswer(message: Message, timeout:Int = 10): Observable<Answer> {
if (!isConnected) {
return Observable.just(Answer.NoConnection)
}
val result = BehaviorSubject.create<Answer>()
val neverDisposed = messages.filter {
it.header.messageId == message.header.messageId
}
.map { Answer.Success(it) as Answer}
.mergeWith(Observable.timer(timeout.toLong(), TimeUnit.SECONDS)
.map { Answer.Timeout })
.take(1).singleOrError()
.subscribe(
{result.onNext(it)},
{
// Should never happen
throw IllegalStateException("Waiting for answer failed: $it")
}
)
sendMessage(message)
return result
}
}
The problem with this solution that "neverDisposed" gets never disposed, is this a memory leak?
My other solutions are not working for this test case:
#Test
fun ImmediateAnswer() {
prepare()
val message = ...
val answerObservable = connection.sendMessageWithAnswer(message, timeout = 1)
connection.receiveMessage(message)
val answer = answerObservable.test()
answer.awaitCount(1)
Thread.sleep(1000)
Assert.assertEquals(1, answer.valueCount())
Assert.assertEquals(Answer.Success(message), answer.values()[0])
}
Do you have a cleaner solution for this problem?

How to make a sync call using RxJava

I need to make a sync call to reauthenticate the user and get a new token, but I haven't found a way that works. The code below blocks the thread and it is never unblocked, ie. I have an infinite loop
class ApolloAuthenticator(private val authenticated: Boolean) : Authenticator {
#Throws(IOException::class)
override fun authenticate(route: Route, response: Response): Request? {
// Refresh your access_token using a synchronous api request
if (response.request().header(HEADER_KEY_APOLLO_AUTHORIZATION) != null) {
return null //if you've tried to authorize and failed, give up
}
synchronized(this) {
refreshTokenSync() // This is blocked and never unblocked
val newToken = getApolloTokenFromSharedPreference()
return response.request().newBuilder()
.header(HEADER_KEY_APOLLO_AUTHORIZATION, newToken)
.build()
}
private fun refreshTokenSync(): EmptyResult {
//Refresh token, synchronously
val repository = Injection.provideSignInRepository()
return repository
.signInGraphQL()
.toBlocking()
.first()
}
fun signInGraphQL() : Observable<EmptyResult> =
sharedPreferencesDataSource.identifier
.flatMap { result -> graphqlAuthenticationDataSource.getAuth(result) }
.flatMap { result -> sharedPreferencesDataSource.saveApolloToken(result) }
.onErrorReturn { EmptyResult() }
}
---------- Use of it
val apollAuthenticator = ApolloAuthenticator(authenticated)
val okHttpBuilder =
OkHttpClient.Builder()
.authenticator(apollAuthenticator)
I haven't found a way to make a sync call using RxJava, but I can make it by using kotlin coutorine runBlocking, which will block the thread until the request is finished:
synchronized(this) {
runBlocking {
val subscription = ApolloReauthenticator.signInGraphQl() // await until it's finished
subscription.unsubscribe()
}
}
fun signInGraphQl(): Subscription {
return repository.refreshToken()
.subscribe(
{ Observable.just(EmptyResult()) },
{ Observable.just(EmptyResult()) }
)
}