背景
IoTと言う言葉が流行るにつれて、MQTTという言葉も注目されている。
来月、おきなわマラソンで、お友達との距離をリアルタイムに把握するシステムを開発しようと思っています。
双方向の通信と言えば、WebSocketでやればいいじゃんという話もありますが、MQTTの特徴であるRetain機能を使えば、アプリ起動時に、お友達の最新の位置情報をすぐ取得できるのではないか?ということで、Android端末でMQTTのクライアント実装を行ってみました。
一応、覚え書きです。
環境
MQTTのサーバとして、時雨堂さんが提供しているsangoのライトプラン
開発環境は、Android Studio 1.5.1 (OSX版)
Android端末は
Google Nexus 5X (Android 6.0.1)
Sony Xperia Z3 (Android 5.0.2)
必要なライブラリ
Android MQTTで検索してみたところ、Paho Android Service が良さそうだったので、これを利用しました。
私は、下記のバイナリファイル
org.eclipse.paho.client.mqttv3-1.0.2.jar
org.eclipse.paho.android.service-1.0.2.jar
をapp/libs フォルダに放りこみました。
このまま、コンパイルすると、リンク時にエラーが発生するので、
android {
... 省略
packagingOptions {
exclude 'META-INF/ECLIPSE_.SF'
exclude 'META-INF/ECLIPSE_.RSA'
}
}
を追記することで、apkファイルがビルド出来ました。
Android Manifestの設定
パーミションとサービスの追記が必要になります
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
... 省略
<service android:name="org.eclipse.paho.android.service.MqttService" >
</service>
</application>
コード例 (subscribe)
public class MainActivity extends AppCompatActivity {
private final String TAG = "Debug";
private MqttAndroidClient mqttAndroidClient;
private String ID = "hoge@github";
private String PASS = "sango";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mqttAndroidClient = new MqttAndroidClient(this, "tcp://lite.mqtt.shiguredo.jp:1883", "d:lite:test:") {
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Bundle data = intent.getExtras();
String action = data.getString("MqttService.callbackAction");
Object parcel = data.get("MqttService.PARCEL");
String destinationName = data.getString("MqttService.destinationName");
if(action.equals("messageArrived"))
{
Log.d(TAG,destinationName + " " + parcel.toString());
}
}
};
try {
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(ID);
options.setPassword(PASS.toCharArray());
mqttAndroidClient.connect(options, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken iMqttToken) {
Log.d(TAG, "onSuccess");
try {
mqttAndroidClient.subscribe("hoge@github/#", 0);
Log.d(TAG, "subscribe");
} catch (MqttException e) {
Log.d(TAG, e.toString());
}
}
@Override
public void onFailure(IMqttToken iMqttToken, Throwable throwable) {
Log.d(TAG, "onFailure");
}
});
} catch (MqttException e) {
Log.d(TAG, e.toString());
}
}
@Override
protected void onPause() {
super.onPause();
try {
if(mqttAndroidClient.isConnected()) {
mqttAndroidClient.disconnect();
Log.d(TAG,"disconnect");
}
mqttAndroidClient.unregisterResources();
} catch (MqttException e) {
Log.d(TAG,e.toString());
}
}
}
コード例 (publish)
btn01.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mqttAndroidClient == null)
return;
try {
if(mqttAndroidClient.isConnected()) {
IMqttDeliveryToken token = mqttAndroidClient.publish("hoge@github/test", "hello world".getBytes(), 0, true);
}
} catch (MqttPersistenceException e) {
Log.d(TAG,e.toString());
} catch (MqttException e) {
Log.d(TAG,e.toString());
}
}
});
動作検証用 (node.jsコード)
これは必要か?わかりませんが、開発中にちょっとしたnode.js向けのコードも書いたので、これも参考までに
共通設定ファイル
{
"host": "lite.mqtt.shiguredo.jp",
"port": 1883,
"options": {
"username": "hoge@github",
"password": "sango",
"keepalive": 10000
},
"topic": "hoge@github"
}
subscribe用
var mqtt = require("mqtt"),
config = require("./config.json"),
client = mqtt.createClient(config.port, config.host, config.options);
client.subscribe(config.topic + "/#");
client.on("message", function(topic, message) {
console.log(topic, message.toString());
});
publish用
var mqtt = require("mqtt"),
config = require("./config.json"),
client = mqtt.createClient(config.port, config.host, config.options);
setInterval(function() {
client.publish(config.topic + "/test", "abc");
}, 30000);
以上、ご参考までに