How to update a field when my app is killed(Firebase) - android

I have an apps where I need to track my user active or not . So I create a service class to update user active status.Here is my service class code
class ServiceClass : Service() {
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Repository.updateActiveStatus(true)
return START_NOT_STICKY
}
override fun onDestroy() {
super.onDestroy()
Repository.updateActiveStatus(false)
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
Log.d("TAGService", "onTaskRemoved: false")
Firebase.firestore.collection("User").document(Firebase.auth.uid.toString())
.update("active", true).addOnSuccessListener {
Log.d("TAGService", "onTaskRemoved: updated")
}
stopSelf()
}}
here is my manifest code
<service android:name=".utils.ServiceClass" android:stopWithTask="false"></service>
But when I open my apps its update its active status but when I destroy or killed my code its not updating.

If you want to check user is active or not then it better to check at application level instead of service level. And if you like to update the UI you can try Event bus.
Here is the sample code.
public class AppOpenManager :Application.ActivityLifecycleCallbacks, LifecycleObserver{
.....
#OnLifecycleEvent(ON_START)
public fun onStart() {
Log.d(LOG_TAG, "onStart");
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public fun onAppBackgrounded() {
Log.d(LOG_TAG,"APP BACKGROUNDED");
}
....
}

Related

How to return response or send data to service from activity from another app ? Android Kotlin

I have a started service app. It intent to activity from another app, but still running in foreground. After a button click in that activity, I want to send data (for example a string "potato") to service without startService() in order to continue, not restart. That's how service keeps running till get the data, while(requiredData != "potato"){}.start. How can I send it, or return response ? I think to use Messenger or Broadcast, but I'm not sure it fits well and how to do.
Note: Service App connected to an activity from another app.
Service App
class RegistryService : Service() {
override fun onBind(p0: Intent?): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val i = packageManager.getLaunchIntentForPackage("com.myexample.potatoactivity")
if (i!=null) {
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(i)
} else {
Toast.makeText(this,"Fail",Toast.LENGTH_SHORT).show()
}
while (true) { // requiredData != "potato"
//Log.d("MyService", "Wait for potato")
}
return START_STICKY
}
}
Potato Activity
class PotatoActivity : AppCompatActivity() {
private lateinit var binding: ActivityPotatoBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPotatoBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding.buttonSendData.setOnClickListener {
//it.putExtra("REQUIRED_DATA", "potato")
}
}
}

How to use MutableSharedFlow in Android Service?

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
}
}

Proper way to unregister a callback from an Application class

I have implemented a custom Application class in my app which handles updating the app theme before the app start up.
I also registered a network callback to set a variable each time there is a connection change.
My application class is as such:
Application.kt
package com.th3pl4gu3.mes.ui
.....
class MesApplication : Application() {
companion object {
#Volatile
private var INSTANCE: MesApplication? = null
fun getInstance() =
INSTANCE ?: synchronized(this) {
INSTANCE
?: MesApplication().also { INSTANCE = it }
}
}
override fun onCreate() {
super.onCreate()
// Assigns 'this' to the singleton object
INSTANCE = this
// Updates the application's theme
updateAppTheme()
// Start a network callback to monitor internet connection
startNetworkCallback()
}
private fun startNetworkCallback(){
try{
val cm = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val builder = NetworkRequest.Builder()
cm.registerNetworkCallback(builder.build(), object: ConnectivityManager.NetworkCallback(){
override fun onAvailable(network: Network) {
super.onAvailable(network)
Log.v("INTERNET_TEST", "AC: Network Available")
Global.isNetworkConnected = true
}
override fun onLost(network: Network) {
super.onLost(network)
Log.v("INTERNET_TEST", "AC: Network Lost")
Global.isNetworkConnected = false
}
})
Global.isNetworkConnected = false
}catch (e: Exception){
Global.isNetworkConnected = false
}
}
}
However, from the docs, they recommend to unregister this callback but the Application class lifecycle doesn't have any onPause or onDestroy function.
Is there any proper way to unregister this callback to not cause any memory leaks?
Also feel free to suggest any alternatives in case I am coding this wrong
In this case , you can use ActivityLifecycleCallbacks, to detect are any Activity of your is in Foreground?
ActivityLiveCycleListener
class ActivityLiveCycleListener(private val appStateListener: AppStateListener) : Application.ActivityLifecycleCallbacks {
companion object {
var foregroundActivities = 0
}
override fun onActivityPaused(p0: Activity) {
}
override fun onActivityStarted(p0: Activity) {
if(foregroundActivities == 0){
appStateListener.onAppForeGround()
}
foregroundActivities++
}
override fun onActivityDestroyed(p0: Activity) {
}
override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {
}
override fun onActivityStopped(p0: Activity) {
foregroundActivities--
if(foregroundActivities == 0){
appStateListener.onAppBackground()
}
}
override fun onActivityCreated(p0: Activity, p1: Bundle?) {
}
override fun onActivityResumed(p0: Activity) {
}
}
And your interface can have two methods to indicate background/foreground state
interface AppStateListener{
fun onAppForeGround()
fun onAppBackground()
}
Now in Application onCreate(), register to ActivityLifeCycleListener
override fun onCreate(){
registerActivityLifecycleCallbacks(ActivityLiveCycleListener(object : AppStateListener{
override fun onAppForeGround() {
//start network listener
}
override fun onAppBackground() {
//remove network listener
}
}))
}

How to solve the error: Not allowed to start service Intent : app is in background uid?

I have a service which started by on booted completed event it, but the app crashes with the error message as in above. Please help on how can I start my Service on BroadCast receiver event of Boot_Completed.
MyService.kt
class MyService : Service() {
override fun onCreate() {
Log.d(TAG, "onCreate")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onDestroy() {
Log.d(TAG, "DO SOME STAFF")
}
}
MyBroadCaster.kt
class StartRelayServiceAtBootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
val serviceIntent = Intent(context, MyService::class.java)
context.startService(serviceIntent)
}
}
}
Upon some searching I got the answer that I had to check the SDK version that I can then start it as foreground service or just with starteService;
class StartRelayServiceAtBootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
val intent = Intent(context, MyService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
Log.i("Autostart", "started")
}
}
}
There are limitations on apps in the background. Obviously, if the device just booted, all apps are "in the background". You cannot start a Service from a background app. You probably need to use JobScheduler to to what you want.
See this document for a discussion about the limitations on background apps and how to migrate to other solutions that are allowed:
https://developer.android.com/about/versions/oreo/background

Fragment onDestroy() vs Service onDestroy()

I need to execute some code when the user swipes up on their recents list (kills the app).
When the app is killed, both my Fragment/Activity and Foreground Service are destroyed.
However, which onDestroy() method should I use to execute the code?
Here is my current setup:
Fragment.kt
override fun onDestroy() {
countDownTimer.cancel()
user1Listener?.remove()
stopService()
super.onDestroy()
mapView?.onDestroy()
}
private fun stopMeetupService(){
val intent = Intent(mContext.applicationContext, MyService::class.java)
activity?.stopService(intent)
}
MyService.kt
val db = FirebaseFirestore.getInstance()
override fun onDestroy() {
super.onDestroy()
sharedPref = this.getSharedPreferences(getString(R.string.user_data), Context.MODE_PRIVATE)
val docId = sharedPref.getString("docId", null)
val docData = mapOf<String, Any?>(
"user2" to "",
"location" to null
)
if (docId != null && docId.isNotBlank()) {
db.collection("docs").document(docId).update(docData).addOnSuccessListener {
}
}
}
Realistically, both sets of code could be merged into the one onDestroy() - ideally whichever method is more reliable to fire.
I guess you should use ActivityLifecycleCallbacks, if you want to stop service only when kill app from recent. Because in some case your service may also stop if you replace the fragments.
So stoping service should Application level and not a Activity/Fragment level in your case
Here is code
AppLifecycleHandler
import android.app.Activity
import android.app.Application
import android.content.ComponentCallbacks2
import android.content.res.Configuration
import android.os.Bundle
class AppLifecycleHandler(private val lifeCycleDelegate: LifeCycleDelegate) : Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
override fun onActivityPaused(p0: Activity?) {
}
override fun onActivityResumed(p0: Activity?) {
}
override fun onActivityStarted(p0: Activity?) {
}
override fun onActivityDestroyed(p0: Activity?) {
lifeCycleDelegate.onAppDestroyed(p0)
}
override fun onActivitySaveInstanceState(p0: Activity?, p1: Bundle?) {
}
override fun onActivityStopped(p0: Activity?) {
}
override fun onActivityCreated(p0: Activity?, p1: Bundle?) {
}
override fun onLowMemory() {}
override fun onConfigurationChanged(p0: Configuration?) {}
override fun onTrimMemory(level: Int) {
}
}
interface LifeCycleDelegate {
fun onAppDestroyed(p0: Activity?)
}
Application class : override onAppDestroyed in Application class and stop service here
class App : LifeCycleDelegate {
....
....
override fun onAppDestroyed(p0: Activity?) {
val intent = Intent(p0, MyService::class.java)
p0?.stopService(intent)
}
}

Categories

Resources