Android でサービスと通信する-Messenger編
3 部作の最後です、Messanger を使ってみます。
アクティビティとハンドラーの定義です。
package red.txn.service_messenger
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
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_messenger.MainActivity.Companion.TAG
import red.txn.service_messenger.ui.theme.ServicemessengerTheme
class MainActivity : ComponentActivity() {
companion object {
const val TAG = "MainActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
ServicemessengerTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
// launch service
Intent(this, MessengerService::class.java).also { intent ->
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
}
private lateinit var messenger: Messenger
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
messenger = Messenger(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(TAG, "onServiceDisconnected()")
}
}
}
class MessageHandler: Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when(msg.what) {
MESSAGE_FROM_SERVICE-> {
// handle the response message
Log.d(TAG, "received: ${msg.data.toString()}")
}
else -> {
Log.d(TAG, "received other: $msg")
}
}
}
}
//***
//*** compsable functions
//***
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
ServicemessengerTheme {
Greeting("Android")
}
}
class Handler はサービスでも参照します。
package red.txn.service_messenger
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
const val MESSAGE_FROM_SERVICE: Int = 2
class MessengerService : Service() {
companion object {
const val TAG = "MessengerService"
}
private val messenger = Messenger(MessageHandler())
override fun onBind(intent: Intent): IBinder {
Log.d(TAG, "onBind()")
Thread {
for (i in 1..2) {
Thread.sleep(2000)
sendMessageFromService()
Log.d(TAG, "sendBroadcast: $i")
}
}.start()
return MyBinder()
}
inner class MyBinder: Binder() {
fun getService(): MessengerService = this@MessengerService
}
private fun sendMessageFromService() {
val message = Message.obtain()
message.what = MESSAGE_FROM_SERVICE
message.data.putString("message", "Hello from MessengerService")
//message.replyTo = messenger
messenger.send(message)
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy(")
}
}
普通はメッセージの場合、普通は双方向通信に使うことが多いようですが、message.replyTo が空でも問題ないようです。
ちょっと試していないのでなんともいえませんがこのサービスももしかすると、startService() 起動に書き換えることができるかもしれません。その場合は Messenger を Activity に渡す必要があるので、LiveData の時と同じようにMessenger を companion object にする必要がありそうです。