とりかく動けばいいんだよ向け。
IntentServiceは別スレッドで動作しているので、UIを操作することができない。なので、例えば下記のようにToastを表示させようとすると表示する場合と表示されない場合がある。
この場合、onStartCommand、onDestroyでは表示されるが、HandleIntent(とそこから呼ばれる関数)からはToastが表示されない。
これはonStartCommandとonDestroyはまでは、アクティビティと同じスレッドて動作していて、onHandleIntentがサービスの本体で別スレッドで動作しているという感じなのだろう。
まずコンパニオンオブジェクトにアクティビティのhandleを入れるプロパティを宣言する。そこにIntentServiceの呼び出し元のアクティビティのスレッドが入っているので、そのスレッドを使ってToastを表示したり、TextViewなどを変更したりする。
Toastはhandler.postを利用して利用したいメソッドを投げればいい。呼び出し元のアクティビティのUIにはアクセスできないので、アクティビティのほうに処理を記述しておき、こちらからメッセージを投げて実行してもらうような処理をする。
呼び出しはメッセージを送ることで行う。Messageはobtainを使用して作成するのがお作法。第三引数には実際に渡すデータを入れる。Any型なので何でも入る。第二引数は処理を区別するためのIntが入る。このIntを受け取った側で判断して処理を行う。
アクティビティには以下のような処理を記述する。
メッセージが送られるとHandleに登録した処理が実行されるので、受け取ったメッセージに従って処理を行う。ここではwhatに第二引数の処理を区別するIntが入り、objにAny型のデータが入るので処理を行う。
一つの部品だけを操作するならView.postを使うと楽。 Handlerと同様に部品のオブジェクトをプロパティとして渡し、postメソッドで操作する。
IntentServiceは別スレッドで動作しているので、UIを操作することができない。なので、例えば下記のようにToastを表示させようとすると表示する場合と表示されない場合がある。
この場合、onStartCommand、onDestroyでは表示されるが、HandleIntent(とそこから呼ばれる関数)からはToastが表示されない。
これはonStartCommandとonDestroyはまでは、アクティビティと同じスレッドて動作していて、onHandleIntentがサービスの本体で別スレッドで動作しているという感じなのだろう。
class SimpleIntentService : IntentService("SimpleIntentService") {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
//表示される
Toast.makeText(this, "ぬるぽ1",Toast.LENGTH_SHORT).show()
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
//表示される
Toast.makeText(this, "ぬるぽ4",Toast.LENGTH_SHORT).show()
super.onDestroy()
}
override fun onHandleIntent(intent: Intent?) {
//表示されない
Toast.makeText(this, "ぬるぽ2",Toast.LENGTH_SHORT).show()
intent?.let {
when (it.action) {
ACTION_TEST -> {
handleActionTEST(intent.extras.getString(EXTRA_PARAM1))
}
}
}
}
private fun handleActionTEST(text: String) {
//表示されない
Thread.sleep(3 * 1000)
Toast.makeText(this, "ぬるぽ3",Toast.LENGTH_SHORT).show()
}
companion object {
private val ACTION_TEST = "jp.test.to.testapplication.action.TEST"
private val EXTRA_PARAM1 = "jp.test.to.testapplication.extra.PARAM1"
fun startActionTEST(context: Context) {
val intent = Intent(context, SimpleIntentService::class.java)
intent.action = ACTION_TEST
intent.putExtra(EXTRA_PARAM1, "がっ")
context.startService(intent)
}
}
}
そこでIntentServiceからアクティビティのUIをいじりたいということで、Handler、Messageを利用する。まずはクラスを以下のように改変する。まずコンパニオンオブジェクトにアクティビティのhandleを入れるプロパティを宣言する。そこにIntentServiceの呼び出し元のアクティビティのスレッドが入っているので、そのスレッドを使ってToastを表示したり、TextViewなどを変更したりする。
Toastはhandler.postを利用して利用したいメソッドを投げればいい。呼び出し元のアクティビティのUIにはアクセスできないので、アクティビティのほうに処理を記述しておき、こちらからメッセージを投げて実行してもらうような処理をする。
呼び出しはメッセージを送ることで行う。Messageはobtainを使用して作成するのがお作法。第三引数には実際に渡すデータを入れる。Any型なので何でも入る。第二引数は処理を区別するためのIntが入る。このIntを受け取った側で判断して処理を行う。
handler?.sendMessage(Message.obtain(handler,1,"がっっ"))
実際に改変したクラスはこのとおり。
class SimpleIntentService : IntentService("SimpleIntentService") {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
//表示される
Toast.makeText(this, "ぬるぽ1", Toast.LENGTH_SHORT).show()
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
//表示される
Toast.makeText(this, "ぬるぽ4", Toast.LENGTH_SHORT).show()
super.onDestroy()
}
override fun onHandleIntent(intent: Intent?) {
//handleを利用してアクティビティのスレッドから実行する
handler?.post(Runnable {
Toast.makeText(this, "ぬるぽ2", Toast.LENGTH_SHORT).show()
})
intent?.let {
when (it.action) {
ACTION_TEST -> {
handleActionTEST(intent.extras.getString(EXTRA_PARAM1))
}
}
}
}
private fun handleActionTEST(text: String) {
Thread.sleep(3 * 1000)
//handleを利用してアクティビティのスレッドから実行する
handler?.post(Runnable {
Toast.makeText(this, "ぬるぽ3", Toast.LENGTH_SHORT).show()
})
//アクティビティの処理を実行するようメッセージを送る
handler?.sendMessage(Message.obtain(handler,1,"がっっ"))
}
companion object {
private val ACTION_TEST = "jp.test.to.testapplication.action.TEST"
private val EXTRA_PARAM1 = "jp.test.to.testapplication.extra.PARAM1"
//親スレッドのハンドルを保持するプロパティ
var handler: Handler? = null
fun startActionTEST(context: Context) {
val intent = Intent(context, SimpleIntentService::class.java)
intent.action = ACTION_TEST
intent.putExtra(EXTRA_PARAM1, "がっ")
context.startService(intent)
}
}
}
アクティビティには以下のような処理を記述する。
メッセージが送られるとHandleに登録した処理が実行されるので、受け取ったメッセージに従って処理を行う。ここではwhatに第二引数の処理を区別するIntが入り、objにAny型のデータが入るので処理を行う。
//アクティビティでハンドルを作る
val handler: Handler = Handler({
when (it.what) {
1 -> {
button.text = it.obj as String
true
}
else -> {
false
}
}
})
//サービスを呼び出す前にhandlerを渡しておくこと
SimpleIntentService.handler = handler
//サービスの実際の呼び出し
SimpleIntentService.startActionTEST(this)
一つの部品だけを操作するならView.postを使うと楽。 Handlerと同様に部品のオブジェクトをプロパティとして渡し、postメソッドで操作する。
override fun onHandleIntent(intent: Intent?) {
//View.postを利用するとスレッドから操作できる
button?.post{ button?.text = "がっ"}
intent?.let {
when (it.action) {
ACTION_TEST -> {
handleActionTEST(intent.extras.getString(EXTRA_PARAM1))
}
}
}
}
companion object {
private val ACTION_TEST = "jp.test.to.testapplication.action.TEST"
private val EXTRA_PARAM1 = "jp.test.to.testapplication.extra.PARAM1"
//親スレッドのハンドルを保持するプロパティ
var handler: Handler? = null
//あらかじめbuttonのオブジェクトをプロパティでもらっておく
var button: Button? = null
fun startActionTEST(context: Context) {
val intent = Intent(context, SimpleIntentService::class.java)
intent.action = ACTION_TEST
intent.putExtra(EXTRA_PARAM1, "がっ")
context.startService(intent)
}
}