Fragment onDestroy() vs Service onDestroy() - android

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

Related

Android background service calls onDestroy() before coroutine finishes work

I have background service in Android to handle Google Firebase Push Notifications:
class MyFirebaseMessagingService : FirebaseMessagingService() {
#Inject
lateinit var repository: Repository
#Inject
lateinit var coroutineDispatchers: CoroutineDispatchers
private val serviceJob = Job()
private lateinit var serviceScope: CoroutineScope
override fun onCreate() {
super.onCreate()
AndroidInjection.inject(this)
serviceScope = CoroutineScope(coroutineDispatchers.default + serviceJob)
}
override fun onNewToken(token: String) {
// Not important
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
serviceScope.launch {
try{
Timber.e(repository.someSuspendMethod())
} catch (e: Exception){
Timber.e(e)
}
}
}
override fun onDestroy() {
super.onDestroy()
serviceJob.cancel()
}
}
My problem is that job is getting cancelled before finishing, because onDestroy() is called. Any idea why Service is killing itself before job is done?
I changed your class to this
class MyFirebaseMessagingService : FirebaseMessagingService(), LifecycleOwner {
private val mDispatcher = ServiceLifecycleDispatcher(this)
#Inject
lateinit var repository: Repository
override fun onCreate() {
mDispatcher.onServicePreSuperOnCreate()
super.onCreate()
}
override fun getLifecycle() = mDispatcher.lifecycle
override fun onNewToken(token: String) {
// Not important
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
lifecycleScope.launch {
try{
Timber.e(repository.someSuspendMethod())
} catch (e: Exception){
Timber.e(e)
}
}
}
override fun onDestroy() {
mDispatcher.onServicePreSuperOnDestroy()
super.onDestroy()
}
}
I use this way to save notification messages in the database

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

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");
}
....
}

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

Good way to pass a reference to a ViewModel in an activity android

I have a ViewModel that inherits from a base class and I would like to have a corresponding Activity also inherit from a base class. The activity would call the same method of the derived ViewModel each time. So it would be something like this:
BaseViewModel:
abstract class BaseViewModel(application: Application) : AndroidViewModel(application) {
protected val context = getApplication<Application>().applicationContext
protected var speechManager: SpeechRecognizerManager? = null
var _actionToTake : MutableLiveData<AnalyseVoiceResults.Actions> = MutableLiveData()
var actionToTake : LiveData<AnalyseVoiceResults.Actions> = _actionToTake
open fun stopListening() {
if (speechManager != null) {
speechManager?.destroy()
speechManager = null
}
open fun startListening() {
val isListening = speechManager?.ismIsListening() ?: false
if (speechManager == null) {
SetSpeechListener()
} else if (!isListening) {
speechManager?.destroy()
SetSpeechListener()
}
}
}
BaseActivity
class BaseActivity : AppCompatActivity() {
private lateinit var baseViewModel: BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun goback() {
super.onBackPressed()
baseViewModel.stopListening()
finish()
}
fun startListening() {
baseViewModel.startListening()
}
override fun onDestroy() {
super.onDestroy()
baseViewModel.stopListening()
}
}
Derived Activity:
class DerivedActivity : BaseActivity() {
private val nextActivityViewModel: NextActivityViewModel by inject()
///^^inherits from BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/*** pass reference ***/
baseViewModel = nexActivityViewModel
nextActivityViewModel.actionToTake.observe(this, object : Observer<AnalyseVoiceResults.Actions?> {
override fun onChanged(t: AnalyseVoiceResults.Actions?) {
if (t?.equals(AnalyseVoiceResults.Actions.GO_BACK) ?: false) {
goback()
}
}
})
startListening()
}
}
Will this cause memory leaks to have two instances of a view model for this activity? Is there a better way to do this? I don't want to keep repeating the same code for all my activities. (I would also have the same question if I was doing this with one base fragment).
make this var baseViewModel: BaseViewModel an abstract variable where all the children class must override it. So, when you call the startListening and stopListening, these methods will be called from children implementation.
Edit:
Make the BaseActivity an abstract class and the baseViewModel as an abstract variable
abstract class BaseActivity : AppCompatActivity() {
private abstract var baseViewModel: BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun goback() {
super.onBackPressed()
baseViewModel.stopListening()
finish()
}
fun startListening() {
baseViewModel.startListening()
}
override fun onDestroy() {
super.onDestroy()
baseViewModel.stopListening()
}
}
So, your DerivedActivity must override the baseViewModel, and every call on father's class will trigger the child
class DerivedActivity : BaseActivity() {
override val baseViewModel: NextActivityViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Will this cause memory leaks to have two instances of a view model for
this activity?
No, there are no memory leaks with this approach. Nor do you have 2 instances of the ViewModel for the same activity. It's a single instance of ViewModel which is being referenced with different variables in BaseActivity and BaseViewModel.
Is there a better way to do this?
The first issue I see is that you have Android specific code in the ViewModels, which is not considered a good practice. You should move the speech manager code to the base activity itself, and ViewModel should only hold the "state" data that you want to retain on orientation changes. This will ensure all the Speech Management methods (create, resume, destroy) will be in the base activity. Concrete activity will only have observers if the state changes.
If you are following any architecture pattern (like MVP), once you move the Speech Manager code out to activity, it would become obvious to move this further out to the Presenter.
EDIT: I have not used the MVVM pattern in production, but this is a light variant of what you may want:
The basic idea is to move Speech management code in a lifecycle-aware component. All UI code in view/activity and business logic / non-android state in viewmodel. I don't see a point in having base activity or viewmodel based on the requirements you have shared so far.
/**
* All the speech related code is encapsulated here, so any new activity/fragment can use it by registering it's lifecycle
*/
class SpeechManager(private val context: Context): LifecycleObserver {
val TAG = "SpeechManager"
private var speechRecognizer: SpeechRecognizer? = null
fun registerWithLifecycle(lifecycle: Lifecycle) {
Log.e(TAG, "registerWithLifecycle")
lifecycle.addObserver(this)
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
Log.e(TAG, "start")
speechRecognizer = (speechRecognizer ?: SpeechRecognizer.createSpeechRecognizer(context)).apply {
// setRecognitionListener(object : RecognitionListener {
// //implement methods
// })
}
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
Log.e(TAG, "stop")
speechRecognizer?.run {
stopListening()
destroy()
}
}
}
ViewModel:
class SpeechViewModel: ViewModel() {
val TAG = "SpeechViewModel"
//List all your "data/state" that needs to be restores across activity restarts
private val actions: MutableLiveData<Actions> = MutableLiveData<Actions>().apply { value = Actions.ActionA }
//Public API for getting observables and all use-cases
fun getActions() = actions
fun doActionA(){
//validations, biz logic
Log.e(TAG, "doActionA")
actions.value = Actions.ActionA
}
fun doActionB(){
Log.e(TAG, "doActionB")
actions.value = Actions.ActionB
}
}
sealed class Actions{
object ActionA: Actions()
object ActionB: Actions()
}
Activity/View:
class SpeechActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_speech)
setSupportActionBar(toolbar)
initSpeechManager()
}
private lateinit var speechManager: SpeechManager
private lateinit var speechViewModel: SpeechViewModel
/**
* Register lifecycle aware components and start observing state changes from ViewModel.
* All UI related code should ideally be here (or your view equivalent in MVVM)
*/
private fun initSpeechManager() {
speechManager = SpeechManager(this).apply {registerWithLifecycle(lifecycle)}
speechViewModel = ViewModelProviders.of(this).get(SpeechViewModel::class.java).apply {
getActions().observe(this#SpeechActivity, Observer<Actions>{
when(it){
is Actions.ActionA -> {
Log.e(TAG, "Perform ActionA")
speechManager.start()
}
is Actions.ActionB -> {
Log.e(TAG, "Perform ActionB")
speechManager.stop()
super.onBackPressed()
}
}
})
}
}
}

How to access Application class variables in activity in kotlin android

I used to access application class private variables by using a
public methods in java
How to do the same using kotlin
App.kt
class App : Application() {
private var app: App? = null
private var movieAppComponent: MovieAppComponent? = null
override fun onCreate() {
super.onCreate()
app = this
movieAppComponent = DaggerMovieAppComponent.builder()
.applicationModule(ApplicationModule(this))
.netModule(NetModule(Keys.BASE_URL, this))
.build()
}
fun getApp(): App? {
return app
}
fun getMovieAppComponent(): MovieAppComponent? {
return movieAppComponent
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
App.getApp().getMovieAppComponent().inject(this)
}
}
Error:
App.getApp().getMovieAppComponent().inject(this)
Here getApp() i am getting as unresolved reference
Solution 1:
class App : Application() {
private var movieAppComponent: MovieAppComponent? = null
companion object {
private var app: App? = null
fun getApp(): App? {
return app
}
}
override fun onCreate() {
super.onCreate()
app = this
movieAppComponent = DaggerMovieAppComponent.builder()
.applicationModule(ApplicationModule(this))
.netModule(NetModule(Keys.BASE_URL, this))
.build()
}
fun getMovieAppComponent(): MovieAppComponent? {
return movieAppComponent
}
}
Solution 2:
No need to create such method. You can use type casting in your Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
(application as? App)?.getMovieAppComponent()?.inject(this)
}
}

Categories

Resources