やりたいこと
AndroidアプリでMQTTのトピックをサブスクライブし、スマホ画面上にメッセージを表示したい。
絵にすると
[外部サーバー(MQTT Broker)] -> [Android(subscriber)]
なぜこんなことをやるのか
外部のMQTTブローカーからのメッセージをサブスクライブし内容確認を行う部分をアプリ化することで、非エンジニアの方にもアプリをインストールさえすれば利用可能にする。
Webアプリ上でサブスクライブも考えたが、認証部分を考えるのが面倒だったのと、Webサーバーを作りたくなかった(手離れを良くしたかった)
補足
参考記事
注意
忘備録ですので、基本的なAndroidアプリの開発方法は書きません。
実装
Android Studio 3.3.2 (OSX版)で実装。
最初に、Empty Activityを作成する。
この Eclipse Paho Android Service
ライブラリが数年前からデファクトのようなので、利用する。
https://github.com/eclipse/paho.mqtt.android
gradle
app の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
<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
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"
を入れて完成。