Android:如何在活动和在不同进程中运行的服务之间进行通信?

问题描述

我在与 service 不同的 process 中开始 activityservice 旨在即使在应用程序关闭时也能运行。 从 service 启动 activity 后,我关闭了应用程序。现在,当我重新打开应用程序时,service 可能正在运行,也可能没有运行。但我还没有办法知道 service 是否正在运行。 我怎样才能做到这一点?

仅供参考:我已经在 SO 上检查了所有相关的答案,但是当服务在不同的进程中运行时,它们都不起作用。 这是我得到的最接近的答案 link。但是这个答案似乎有缺陷,我也想听听您的意见。

这是我目前正在做的事情:

AndroidManifest.xml

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

MainApplication.kt(目的:只有一个 SettingsRepository 类的实例)

class MainApplication : Application() {

   val settingsRepository by lazy { SettingsRepository(this) }

}

SettingsRepository.kt(目的:在 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)
       }
   }
}

解决方法

使用 Messenger 类与服务器 https://developer.android.com/reference/android/app/Service.html#remote-messenger-service-sample

通信

或者使用 BroadcastReceiver 从另一个进程获取服务状态

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

您可以在服务中再注册一个接收器,以便从活动中 ping 它。

关于它仅适用于单个流程应用程序的最接近的答案