0

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)
       }
   }
}
Junior
  • 170
  • 2
  • 10

1 Answers1

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

guest
  • 432
  • 3
  • 8