とりかく動けばいいんだよ向け。

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