Android でサービスと通信する-LiveData編
その 2 今度は LiveData を使ってみます。
LiveData を使うならこんな感じ
package red.txn.service_livedata
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import red.txn.service_livedata.ui.theme.ServicelivedataTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
ServicelivedataTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
// launch service
Intent(this, LiveDataService::class.java).also { intent ->
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
}
private var bound = false
private lateinit var liveDataService: LiveDataService
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder) {
val binder = service as LiveDataService.MyBinder
liveDataService = binder.getService()
bound = true
observeData()
}
override fun onServiceDisconnected(name: ComponentName?) {
bound = false
}
}
fun observeData() {
liveDataService.BUS.observe(this) { newData ->
Log.d("AAA", "Activity:" + newData.toString())
}
}
}
//***
//*** Composable functions
//***
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
ServicelivedataTheme {
Greeting("Android")
}
}
bindService() でサービス起動をしているので少し様子が異なるが、バインドしたとき observeData() で監視している。とっても簡単
package red.txn.service_livedata
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class LiveDataService : Service() {
companion object {
const val TAG = "LiveDataService"
}
val BUS = MutableLiveData<String>()
override fun onBind(intent: Intent): IBinder {
Log.d(TAG, "serviee onBind()")
Thread {
for (i in 1..2) {
Thread.sleep(2000)
updateData("データ$i")
Log.d(TAG, "データ$i")
}
}.start()
val scope = CoroutineScope(Dispatchers.IO)
scope.launch {
for (i in 1..2) {
Thread.sleep(2000)
updateData("データ2$i")
Log.d(TAG, "データ2$i")
}
}
return MyBinder()
}
inner class MyBinder: Binder() {
fun getService(): LiveDataService = this@LiveDataService
}
private fun updateData(newData: String) {
BUS.postValue(newData)
}
}
AndroidManifest.xml は生成されたものなので省略。
startService() でサービス起動したい場合は、こんなふうになった。
package red.txn.service_livedata
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import red.txn.service_livedata.ui.theme.ServicelivedataTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
ServicelivedataTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
// launch service
Intent(this, LiveDataService::class.java).also { intent ->
startService(intent)
}
LiveDataService.BUS.observe(this) {
Log.d("BUS change:", "Activity:" + it.toString())
}
}
}
//***
//*** Composable functions
//***
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
ServicelivedataTheme {
Greeting("Android")
}
}
サービスの方では LiveData を companion object に入れてインスタンスなしでも取得できるようにしている。
package red.txn.service_livedata
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class LiveDataService : Service() {
companion object {
const val TAG = "LiveDataService"
val BUS = MutableLiveData<String>()
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
Thread {
for (i in 1..2) {
Thread.sleep(2000)
BUS.postValue("newData: $i")
}
}
return START_STICKY
}
}
Gemini を使ってみているが、前の文脈からの続きの質問したら流れをぶった切って新しい話題のように回答するのやめてほしい。Claude のほうがその辺楽かも。