#サンプルコード
ActivityとServiceを1つずつ立ち上げてmessengerでやり取りするサンプルです。
意外とkotlinのサンプルがなかったので、書いてみました。
走り書きですが、よければコピペして使ってください。
※Qiita上でベタベタと切り貼りしたので、貼り付けるだけだと動かないかもしれません。
この中身を解説していきます。
コード全量はこちら。
package com.satken.randomwikireader
import android.content.ComponentName
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.content.Intent
import android.content.ServiceConnection
import android.widget.Button
import android.os.*
import java.lang.ref.WeakReference
const val MSG_ACTIVITY_TO_SERVICE = 1
const val MSG_SERVICE_TO_ACTIVITY = 2
class MainActivity : AppCompatActivity() {
private var bound = false
private var mActivityMessenger: Messenger? = null
private var mServiceMessenger: Messenger? = null
private var mActivityHandler: ActivityHandler? = null
private val mConnection = object: ServiceConnection {
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
mServiceMessenger = Messenger(binder)
try {
val msg = Message.obtain(null, MSG_ACTIVITY_TO_SERVICE, 0, 0)
msg.replyTo = mActivityMessenger
mServiceMessenger!!.send(msg)
} catch (e: RemoteException) {
e.printStackTrace()
}
bound = true
}
override fun onServiceDisconnected(name: ComponentName?) {
mServiceMessenger = null
bound = false
}
}
internal class ActivityHandler(
activity: MainActivity
) : Handler() {
//define weak reference to activity
private val mActivity = WeakReference<MainActivity>(activity)
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_SERVICE_TO_ACTIVITY ->{
//something
}
else -> super.handleMessage(msg)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mActivityHandler = ActivityHandler(this)
mActivityMessenger = Messenger(mActivityHandler)
findViewById<Button>(R.id.start).setOnClickListener {
if(bound) {
try {
val msg = Message.obtain(null, MSG_STOP_UTTERANCE, 0, 0)
msg.replyTo = mActivityMessenger
mServiceMessenger!!.send(msg)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
}
//start service
Intent(this, MyService::class.java).also {
it.action = action
//use startForgroundService in Android-O
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(it)
return
}
startService(it)
}
}
override fun onStop() {
super.onStop()
//unbind from the service
if(bound){
unbindService(mConnection)
bound = false
}
}
override fun onStart(){
super.onStart()
//bind to the service
Intent(this, MyService::class.java).also{ intent ->
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
}
}
}
package com.satken.randomwikireader
import android.app.Service
import android.content.Intent
import android.os.*
import java.lang.ref.WeakReference
class MyService: Service() {
private var mServiceMessenger: Messenger? = null
internal class ServiceHandler(
service: MyService
) : Handler() {
private val mService = WeakReference<MyService>(service)
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_ACTIVITY_TO_SERVICE ->{
//do something
//return some value
try {
val msg2 = Message.obtain(null, MSG_SERVICE_TO_ACTIVITY, 0, 0)
msg.replyTo.send(msg2)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
else -> super.handleMessage(msg)
}
}
}
override fun onBind(intent: Intent?): IBinder {
mServiceMessenger = Messenger(ServiceHandler(this))
return mServiceMessenger!!.binder
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return Service.START_NOT_STICKY
}
}
#Serviceを起動する
サービスを起動する時は、Intentを使って以下のように呼び出します。
2行目から6行目は、別になくても大丈夫です。
API level26 以降はstartForegroundService
が使えます。
Intent(this, MyService::class.java).also {
it.action = action
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(it)
return
}
startService(it)
}
#Activityとの関連付け
起動したサービスに対して何かActivityから情報を伝える方法として、Messengerを使うことができます。
他にも方法はありますが、これが手軽です。
##Connectionの定義
ActivityにはConnection
のインスタンスを作成しておきます。
サービスをbindする時はこのインスタンスをリスナーとして指定します。
private val mConnection = object: ServiceConnection {
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
mServiceMessenger = Messenger(binder)
bound = true
}
override fun onServiceDisconnected(name: ComponentName?) {
mServiceMessenger = null
bound = false
}
}
onServiceConnected
では、IBinderからMessengerを作成しています。
ここで取得したmServiceMessenger
は後で相互に通信する時に使います。
##Bind
bindするタイミングはonStartがよさそうです。
override fun onStart(){
super.onStart()
//bind to the service
Intent(this, MyService::class.java).also{ intent ->
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
}
}
##Serviceとの通信
###Activity→Service
通信を受ける側では、ハンドラークラスを定義し、onBindでインスタンスを作ります。
internal class ServiceHandler(
service: MyService
) : Handler() {
private val mService = WeakReference<TTSService>(service)
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_ACTIVITY_TO_SERVICE ->{
}
else -> super.handleMessage(msg)
}
}
}
override fun onBind(intent: Intent?): IBinder {
mServiceMessenger = Messenger(ServiceHandler(this))
return mServiceMessenger!!.binder
}
通信を送る側では、さきほどonServiceConnected
で取得したMessengerに対してMessageを送ります。
MessageにreplyTo
をして、返信先のMessengerを指定できます。
自身のActivityで作ったMessengerを指定しておきましょう。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mActivityMessenger = Messenger(ActivityHandler(this))
}
fun sendMesasge(){
try {
val msg = Message.obtain(null, MSG_ACTIVITY_TO_SERVICE, 0, 0)
msg.replyTo = mActivityMessenger
mServiceMessenger!!.send(msg)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
###Service→Activity
サービス側で何か処理をして、結果をActivityに返すには同様にMessengerを使用します。
さきほどmsgにreplytoを指定したので、そこに対して送ればAcitivityに戻ります。
override fun handleMessage(msg: Message) {
try {
val msg2 = Message.obtain(null, MSG_SERVICE_TO_ACTIVITY, 0, 0)
msg.replyTo.send(msg2)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
##Unbind
bindしたサービスはonStopとかでunbindしておきましょう。
override fun onStop() {
super.onStop()
//unbind from the service
if(bound){
unbindService(mConnection)
bound = false
}
}