AndroidでMQTTのトピックをサブスクライブし、スマホ画面上にメッセージを表示

# やりたいこと
AndroidアプリでMQTTのトピックをサブスクライブし、スマホ画面上にメッセージを表示したい。

### 絵にすると
[外部サーバー（MQTT Broker）] -> [Android(subscriber)]

### なぜこんなことをやるのか
外部のMQTTブローカーからのメッセージをサブスクライブし内容確認を行う部分をアプリ化することで、非エンジニアの方にもアプリをインストールさえすれば利用可能にする。
Webアプリ上でサブスクライブも考えたが、認証部分を考えるのが面倒だったのと、Webサーバーを作りたくなかった（手離れを良くしたかった）

# 補足
### 参考記事
https://qiita.com/KazuyukiEguchi/items/c67524e8b3c9c6459b2d
### 注意
忘備録ですので、基本的なAndroidアプリの開発方法は書きません。

# 実装
Android Studio 3.3.2 (OSX版）で実装。

最初に、Empty Activityを作成する。

この `Eclipse Paho Android Service` ライブラリが数年前からデファクトのようなので、利用する。
https://github.com/eclipse/paho.mqtt.android

### gradle
app のGradleに以下を追加

``` gradle
repositories {
    maven {
        url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
    }
}

dependencies {
    implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.0'
    implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'

```

### manifest
``` xml
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
     ...
        <service android:name="org.eclipse.paho.android.service.MqttService">
        </service>
    </application>
```

### MainActivity.kt
``` kotlin
package me.oxoxo.mqttsubscriber

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import kotlinx.android.synthetic.main.activity_main.*
import org.eclipse.paho.android.service.MqttAndroidClient
import org.eclipse.paho.client.mqttv3.*

class MainActivity : AppCompatActivity() {

    private lateinit var mqttAndroidClient: MqttAndroidClient

    private val clientId = System.currentTimeMillis()
    private val serverUri = "tcp://<your-endpoint>:1883"
    private val subscriptionTopic = "<your-topic>"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mqttAndroidClient = MqttAndroidClient(applicationContext, serverUri, clientId.toString())
        mqttAndroidClient.setCallback(object : MqttCallbackExtended {
            override fun connectComplete(reconnect: Boolean, serverURI: String) {

                if (reconnect) {
                    addToHistory("Reconnected to : $serverURI")
                    subscribeToTopic()
                } else {
                    addToHistory("Connected to: $serverURI")
                }
            }

            override fun connectionLost(cause: Throwable) {
                addToHistory("The Connection was lost.")
            }

            @Throws(Exception::class)
            override fun messageArrived(topic: String, message: MqttMessage) {
                addToHistory("Incoming message: " + String(message.payload))

                hello.text = String(message.payload)
            }

            override fun deliveryComplete(token: IMqttDeliveryToken) {}
        })

        val mqttConnectOptions = MqttConnectOptions()
        mqttConnectOptions.isAutomaticReconnect = true
        mqttConnectOptions.isCleanSession = false
        mqttConnectOptions.userName = "<user_name>"
        mqttConnectOptions.password = "<password>".toCharArray()

        try {
            mqttAndroidClient.connect(mqttConnectOptions, null, object : IMqttActionListener {
                override fun onSuccess(asyncActionToken: IMqttToken) {
                    val disconnectedBufferOptions = DisconnectedBufferOptions()
                    disconnectedBufferOptions.isBufferEnabled = true
                    disconnectedBufferOptions.bufferSize = 100
                    disconnectedBufferOptions.isPersistBuffer = false
                    disconnectedBufferOptions.isDeleteOldestMessages = false
                    mqttAndroidClient.setBufferOpts(disconnectedBufferOptions)
                    subscribeToTopic()
                }

                override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
                    addToHistory("Failed to connect to: $serverUri")
                    exception.printStackTrace()
                }
            })

        } catch (ex: MqttException) {
            ex.printStackTrace()
        }
    }

    fun subscribeToTopic() {
        try {
            mqttAndroidClient.subscribe(subscriptionTopic, 0, null, object : IMqttActionListener {
                override fun onSuccess(asyncActionToken: IMqttToken) {
                    addToHistory("Subscribed!")
                }

                override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
                    addToHistory("Failed to subscribe")
                }
            })

        } catch (ex: MqttException) {
            System.err.println("Exception whilst subscribing")
            ex.printStackTrace()
        }
    }

    private fun addToHistory(mainText: String) {
        Log.d("tag","LOG: $mainText")
    }
}

```

endpoint, topic, user name, password は、環境に合わせて入力する。
activity_main.xmlのTextViewに、 `android:id="@+id/hello"
`を入れて完成。