1

I am starting a service in a different process from an activity. The service is designed to run even when the app is closed. After starting the service from the activity, I close the app. Now when I reopen the app the service may or may not be running. But I haven't find way to know if the service is running or not. How can I achieve that?

FYI: I have checked all the related answers here on SO but none of them works when the service is running in a different process. This is the closest answer I have got link. But this answer seems flawed, I would also like to hear your opinion on it too.

Here's what I am currently doing:

AndroidManifest.xml

<service
        android:name=".services.MyService"
        android:enabled="true"
        android:exported="false"
        android:process=":backgroundProcess" />

MainApplication.kt (purpose: to have only one instance of the SettingsRepository class)

class MainApplication : Application() {

   val settingsRepository by lazy { SettingsRepository(this) }

}

SettingsRepository.kt (purpose: to save the running state of the service in Preference DataStore)

class SettingsRepository(context: Context) {

    private val dataStore = context.createDataStore(name = "settings_prefs")

    companion object {
        val SERVICE_STATE_KEY = booleanPreferencesKey("SERVICE_STATE_KEY")
    }

    suspend fun saveServiceStateToDataStore(state: Boolean) {
        dataStore.edit {
            it[SERVICE_STATE_KEY] = state
        }
    }

    val getServiceStateFromDataStore: Flow<Boolean> = dataStore.data.map {
        val state = it[SERVICE_STATE_KEY] ?: false
        state
    }

}

Service.kt

private lateinit var settingsRepository: SettingsRepository

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

    settingsRepository = (application.applicationContext as MainApplication).settingsRepository
    
    saveStateToDataStore(true)

    return START_REDELIVER_INTENT
}

private fun saveStateToDataStore(state: Boolean): Job {

    return CoroutineScope(Dispatchers.IO).launch {
        settingsRepository.saveServiceStateToDataStore(state)
    }
}

Activity.kt

private fun observeDataFromViewModel() {
    mainViewModel.readServiceStateFromRepository.observe(this, {state ->
        Toast.makeText(this, "Service state changed to $state", Toast.LENGTH_SHORT).show()

        // should get the new data when service stores it in onStartCommand but doesn't get it
        // maybe because the service doesn't stores the data for some reason I am not aware of.
       
    })
    
}
private fun handleClickListener() {
    btn_start_service.setOnClickListener {
            startForegroundService(serviceIntent)
        }
    }

    btn_stop_service.setOnClickListener {
        mainViewModel.saveServiceState(false)
        stopService(serviceIntent)
    }
}

ViewModel.kt

class MainViewModel(application: Application) : AndroidViewModel(application) {

   private val settingsRepository = (application.applicationContext as MainApplication).settingsRepository

   val readServiceStateFromRepository = settingsRepository.getServiceStateFromDataStore.asLiveData()


   fun saveServiceState(state: Boolean): Job {
       return viewModelScope.launch(Dispatchers.IO) {
           settingsRepository.saveServiceStateToDataStore(state)
       }
   }
}

1 Answer 1

0

Use the Messenger class to communicate with server https://developer.android.com/reference/android/app/Service.html#remote-messenger-service-sample

Or use a BroadcastReceiver to get service state from another process

class MainActivity : AppCompatActivity() {
    private var receiver: TmpReceiver? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.view_main)
        val intent = Intent(this, TmpService::class.java)
        receiver = TmpReceiver()
        val filter = IntentFilter().apply {
            addAction("SERVICE_START")
            addAction("SERVICE_STOP")
        }
        registerReceiver(receiver, filter)
        startService(intent)
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(receiver)
        receiver = null
    }
}

class TmpService : Service() {
    override fun onBind(p0: Intent?): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        sendBroadcast("SERVICE_START")
    }

    override fun onDestroy() {
        sendBroadcast("SERVICE_STOP")
        super.onDestroy()
    }

    private fun sendBroadcast(action: String) {
        Intent().also { intent ->
            intent.action = action
            sendBroadcast(intent)
        }
    }
}

class TmpReceiver: BroadcastReceiver() {
    override fun onReceive(p0: Context?, p1: Intent?) {
        Log.d("TmpReceiver", "action=${p1?.action}")
    }
}

You can register one more receiver into the service to ping it from the activity.

About the closest answer it works for a single process app only

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.