I want to insert some records which I get from the API to my database,
I am using a service class to do this process, I was trying to use this concept of live data inside service class, but it required my service class to be a lifecycleowner.
am stuck with how to make my service class observer to the changes in a live data
Any help will be good!!
If your service should not be affected by activity lifecycle (onStop(), onStart() etc) then you can use LiveData<T>.observeForever(Observer<T>) method. Like so,
val observer = Observer<YourDataType> { data ->
//Live data value has changed
}
liveData.observeForever(observer)
To stop observing you will have to call LiveData.removeObserver(Observer<T>). Like so:
liveData.removeObserver(observer)
If you need to stop observing when the application is in the background, you can bind your service in the calling activity in the onStart() method of the activity and unbind your service in the onStop() method. Like so:
override fun onStart() {
super.onStart()
val serviceIntent = Intent(this, myService::class.java)
bindService(serviceIntent, myServiceConnection, Context.BIND_AUTO_CREATE)
}
override fun onStop() {
unbindService(myServiceConnection)
super.onStop()
}
Read on bound services here
Then, in the service
override onBind(Intent) and onRebind(Intent) method and start observing the LiveData (App is in foreground)
override fun onBind(intent: Intent?): IBinder? {
liveData.observeForever(observer)
return serviceBinder
}
override fun onRebind(intent: Intent?) {
liveData.observeForever(observer)
super.onRebind(intent)
}
Remove LiveData observer in onUnbind(Intent) (App is in background)
override fun onUnbind(intent: Intent?): Boolean {
liveData.removeObserver(observer)
return true
}
Related
Hi and thank you for your expertise.
I am trying to run a service which is going to display a notification and after some time check if it is still there or user has got rid of it.
So in the MainActivity.kt when onPause function is triggered I start the service like this
startService(Intent(this,NewService::class.java))
then in the NewService.kt I would like to check using MainActivity.kt function checkNotifications() if there is a notification displayed
class NewService:Service() {
#RequiresApi(Build.VERSION_CODES.M)
override fun onStartCommand(init : Intent, flag : Int, startId: Int):Int{
MainActivity().checkNotifications()
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
}
This is what checkNotifications() function in MainActivity.kt looks like
#RequiresApi(Build.VERSION_CODES.M)
fun checkNotifications(){
val notifications: Array<StatusBarNotification> = notificationManager.activeNotifications
if(notifications.isNotEmpty())
{
println("Notification exists")
}
else
{
println("No notifications")
}
}
So my app works 'fine' as long as I don't call checkNotifications(). What I mean the service starts and so on. However when I try to call checkNotifications() I get this error
java.lang.RuntimeException: Unable to start service abc.com.app.NewService#b380e9f with Intent { cmp=abc.com.app/.NewService }: kotlin.UninitializedPropertyAccessException: lateinit property notificationManager has not been initialized
The property notificationManager is initialized in onCreate() function and everyting is working up until I call checkNotifications()
Would anyone be so kind and tell me what am I doing wrong?
Thank You
MainActivity().checkNotifications()
Never create an instance of an activity yourself. Move checkNotifications() into the service or into some utility class (where you provide a Context as a parameter to checkNotifications() for the purposes of obtaining a NotificationManager).
Since AsyncTask, IntentSerrvice and JobIntentService are all deprecated, which tool or class should I go for in 2022?
I want to re-schedule alarms in a BroadcastReceiver after a device rebooted (since alarms get lost in the process). The task will most probably take < 1 min to finish. I just need the safety of it completing and not being killed off by the system.
The documentation on Broadcasts shows an (outdated) example with goAsync() and the deprecated AsyncTask.
But it also mentions JobService. Is that the replacement? What about WorkManager?
goAsync() return a PendingIntent - it mean you ask for android system extend time life of Broadcast receiver => goAsync() is used for short background task.
Life time of BroadcastReceiver is very short, so... for long time background task, you must to change to other context has longer life time, such as: Service, JobService..etc.
Example:
BroadcastReceiver received intent
BroadcastReceiver start a service, run worker thread to process long time task
after worker thread finish, call finish service too
=========================================
class MyIntentService : Service() {
private val handleThread = HandlerThread("MyThread")
private lateinit var workerHandler: Handler
override fun onCreate() {
super.onCreate()
handleThread.start()
workerHandler = Handler(handleThread.looper)
}
override fun onDestroy() {
workerHandler.removeCallbacksAndMessages(null)
handleThread.quitSafely()
super.onDestroy()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val data = intent?.data
workerTask(data)
return START_NOT_STICKY
}
private fun workerTask(data: Uri?) {
workerHandler.post {
heavyTask(data)
finishMyIntentService()
}
}
private fun finishMyIntentService() {
stopSelf()
}
private fun heavyTask(data: Uri?) {
// to do heavyTask example
for (i in 1..20)
{
Log.d("test","#heavyTask() $i")
Thread.sleep(1000)
}
}
override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented")
}
}
then startService from BroadCastReceiver
I want to use MutableSharedFlow in the Service class, but I'm not sure how to stop subscribing when Service ends. How to implement the MutableSharedFlow function in service or any other function available to listen to stream data?
To use a Flow in an android Service class we need a CoroutineScope instance to handle launching coroutines and cancellations. Please see the following code with my comments:
class CoroutineService : Service() {
private val scope = CoroutineScope(Dispatchers.IO)
private val flow = MutableSharedFlow<String>(extraBufferCapacity = 64)
override fun onCreate() {
super.onCreate()
// collect data emitted by the Flow
flow.onEach {
// Handle data
}.launchIn(scope)
}
override fun onStartCommand(#Nullable intent: Intent?, flags: Int, startId: Int): Int {
scope.launch {
// retrieve data from Intent and send it to Flow
val messageFromIntent = intent?.let { it.extras?.getString("KEY_MESSAGE")} ?: ""
flow.emit(messageFromIntent)
}
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onDestroy() {
scope.cancel() // cancel CoroutineScope and all launched coroutines
}
}
I use foreground service for playing music.
I've done research and figured out that process keep living even if I call stopForeground(true) and stopSelf(). It doesn't seem to work if I call it from the onDestroy of my activity. But if I call them from another place for example when user clicks button, it works.
But how to do that my service to be killed when app is killed.
private const val EMPTY_ROOT = "emptyRoot"
private val TAG = SoundsService::class.java.name
class SoundsService : MediaBrowserServiceCompat() {
#Inject
lateinit var mediaSession: MediaSessionCompat
#Inject
lateinit var callback: MediaSessionCallback
override fun onCreate() {
inject()
super.onCreate()
mediaSession.apply {
setCallback(callback)
isActive = true
}
sessionToken = mediaSession.sessionToken
}
override fun onGetRoot(
clientPackageName: String,
clientUid: Int,
rootHints: Bundle?
): BrowserRoot? {
return BrowserRoot(EMPTY_ROOT, null)
}
override fun onLoadChildren(
parentId: String,
result: Result<MutableList<MediaBrowserCompat.MediaItem>>
) {}
override fun onDestroy() {
mediaSession.run {
isActive = false
release()
}
stopForeground(false)
stopSelf()
super.onDestroy()
}
}
It will work in the onDestroy. Just make sure your are calling it, before super call. And use ApplicationContenxt for that as well.
applicationContext.startForegroundService(Intent(this, ServiceTest::class.java));
applicationContext.stopService(Intent(this, ServiceTest::class.java))
Have you checkt for any crashes when you call stopForeground(true) or stopSelf()
How do i recall an Intent in Android Studio?
I am trying to build a service which uses an intent. In some case, I am trying to send a broadcast up to a specific number of times( let's say X times) if intent is not received by the activity. After X times, still activity does not get the intent , i want to delete that intent and do some other operation for that activity.
As a way to implement this, you can broadcast EventBus events through JobQueue job, that can retry sending of an event for needed number of times. The subscriber activity subscribed to this event, and creates your Intent only in case if event was handled.
From code perspective this could be implemented as:
Event, that will be broadcasted:
class SomeEvent(val isSuccess: Boolean)
Job class, which instance is initiated by your service:
class SendCustomEventJob() : Job{
#Inject
#Transient
lateinit var eventBus: EventBus
// dagger jobs component injection
override fun inject(appComponent: AppComponent) {
super.inject(appComponent)
appComponent.inject(this)
}
override fun onRun() {
// some logic goes here, like API calls
eventBus.post(SomeEvent(true))
}
override fun shouldReRunOnThrowable(throwable: Throwable, runCount: Int, maxRunCount: Int): RetryConstraint {
if(runCount == 3)
// your custom job exceptions handling logic
}
override fun getRetryLimit(): Int = 3 // your custom retries number
override fun onAdded() { }
override fun onCancel(cancelReason: Int, throwable: Throwable?) {
eventBus.post(SomeEvent(false))
}
}
And handling of events in your activity-subscriber:
#Subscribe(threadMode = ThreadMode.MAIN)
fun onFetchedCustomEvent(event: SomeEvent) {
if (!event.isSuccess)
return
//create your intent here...
}