本文旨在构建一个条理清晰的Android MQTT通信的demo,希望能够提供一定的帮助,文末附代码
先看效果图
前言
我相信对于大部分Android开发人员来说,MQTT服务是最大的坎
不过可以尝试在自己电脑上搭建mqtt服务,我参考的是这一篇博客:奋斗鱼 MQTT:windows最简单搭建mqtt服务端及本地客户端测试
按照博客里面的讲解逐条操作,最终我们可以在自己电脑上创建一个MQTT的服务,并且可以通过浏览器访问,如下图
如果我们想测试用浏览器发送消息,然后用app去接的话,可以这么来操作
其实浏览器端不一定需要连接电脑的ip,可以直接连接127.0.0.1也就是本机ip,同样可以连接到本机的MQTT服务;
当浏览器也连接到MQTT服务之后,因为app端订阅的就是“swy”的topic的信息,所以当发布topic为“swy”的信息时,app端就可以接到消息了。
那么服务端就算是有了,接下来就开始着手做Android客户端
1.引入依赖
//MQTT
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
2.申请权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
3.注册服务
<application
......
<service android:name="org.eclipse.paho.android.service.MqttService"/>
</application>
说明:
为了避免出现:
java.lang.NoClassDefFoundError: Failed resolution of: Landroid/support/v4/content/LocalBroadcastManager;
可以在gradle.properties文件中增加
android.useAndroidX=true
android.enableJetifier=true
4.布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_connect"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="建立连接" />
<Button
android:id="@+id/btn_subscribe"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="订阅Topic"
android:textAllCaps="false" />
<Button
android:id="@+id/btn_echo"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Echo测试"
android:textAllCaps="false" />
<Button
android:id="@+id/btn_disconnect"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="断开连接" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
<ScrollView
android:id="@+id/scroll_deal"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@+id/tv_deal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="操作:\n"
android:textColor="@color/black" />
</ScrollView>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#dcdcdc" />
<ScrollView
android:id="@+id/scroll_record"
android:layout_width="match_parent"
android:layout_marginTop="5dp"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@+id/tv_record"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="记录:\n"
android:textColor="@color/black" />
</ScrollView>
</LinearLayout>
<Button
android:id="@+id/btn_clear"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="清空" />
</LinearLayout>
说明:为了更方便的操作控件,使用了ViewBinding
build.gradle文件
android {
...
defaultConfig {
...
viewBinding{
enabled true
}
}
}
5.主界面
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import com.swy.mqttdemo.databinding.ActivityMainBinding;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private String TAG = "swyLog";
private ActivityMainBinding binding;
private MqttAndroidClient mqttAndroidClient;
private MqttConnectOptions mqttConnectOptions;
private String mqttUrl = "tcp://192.168.103.42:1883";
private String clientId = "shaowangyun";
private String topic = "swy";
String testPubilish = "ECHO ECHO";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
initMqttClient();
binding.btnConnect.setOnClickListener(v -> {
connect();
});
binding.btnSubscribe.setOnClickListener(v -> {
subscribe();
});
binding.btnEcho.setOnClickListener(v -> {
echo();
});
binding.btnDisconnect.setOnClickListener(v -> {
disconnect();
});
binding.btnClear.setOnClickListener(v -> {
binding.tvDeal.setText("操作:\n");
binding.tvRecord.setText("记录:\n");
});
}
private void initMqttClient() {
mqttAndroidClient = new MqttAndroidClient(this, mqttUrl, clientId);
mqttAndroidClient.setCallback(new MqttCallback());
mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setAutomaticReconnect(false);
}
private void connect() {
if (mqttAndroidClient != null && mqttAndroidClient.isConnected()) {
return;
}
Log.i(TAG,"建立连接");
try {
mqttAndroidClient.connect(mqttConnectOptions, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
appendStatus("连接成功");
}
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
appendStatus("连接失败");
}
});
} catch (MqttException e) {
Log.i(TAG, "connect Exceptions : " + e);
e.printStackTrace();
}
}
private void subscribe() {
if (mqttAndroidClient == null || !mqttAndroidClient.isConnected()) {
return;
}
Log.i(TAG,"订阅topic");
try {
mqttAndroidClient.subscribe(topic, 2, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
appendStatus( "订阅\""+topic+"\"成功");
}
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
appendStatus(topic + "订阅失败");
}
});
} catch (MqttException e) {
Log.i(TAG, "subscribe Exceptions : " + e);
e.printStackTrace();
}
}
private void echo() {
if (mqttAndroidClient == null || !mqttAndroidClient.isConnected()) {
return;
}
Log.i(TAG,"echo测试");
MqttMessage message = new MqttMessage();
message.setQos(2);
message.setPayload(testPubilish.getBytes());
try {
mqttAndroidClient.publish(topic, message, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
appendStatus("消息发送成功");
}
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
appendStatus("消息发送失败");
}
});
} catch (MqttException e) {
Log.i(TAG, "echo Exceptions : " + e);
e.printStackTrace();
}
}
private void disconnect() {
if (mqttAndroidClient == null || !mqttAndroidClient.isConnected()) {
return;
}
Log.i(TAG,"断开连接");
try {
mqttAndroidClient.disconnect();
} catch (MqttException e) {
Log.i(TAG, "disconnect Exceptions : " + e);
e.printStackTrace();
}
}
public class MqttCallback implements MqttCallbackExtended {
@Override
public void connectComplete(boolean reconnect, String serverURI) {
if (reconnect) {
appendStatus("重连成功");
} else {
appendStatus("初始化成功");
}
}
@Override
public void connectionLost(Throwable cause) {
appendStatus("连接断开");
}
@Override
public void messageArrived(String topic, MqttMessage message) {
appendMessage(1, new String(message.getPayload()));
appendStatus("消息接收成功");
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
appendMessage(0, testPubilish);
}
}
private void appendStatus(String msg) {
binding.tvDeal.append(getCurrentTime() + " " + msg + "\n");
binding.scrollDeal.post(() -> binding.scrollDeal.fullScroll(View.FOCUS_DOWN));
binding.scrollRecord.post(() -> binding.scrollRecord.fullScroll(View.FOCUS_DOWN));
}
//0:发送
//1:接收
private void appendMessage(int type, String msg) {
if (0 == type) {
binding.tvRecord.append("发送消息:" + msg + "\n");
} else {
binding.tvRecord.append("接收消息:" + msg + "\n");
}
binding.scrollDeal.post(() -> binding.scrollDeal.fullScroll(View.FOCUS_DOWN));
binding.scrollRecord.post(() -> binding.scrollRecord.fullScroll(View.FOCUS_DOWN));
}
private String getCurrentTime() {
Date currentTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm:ss");
return sdf.format(currentTime);
}
@Override
protected void onDestroy() {
super.onDestroy();
disconnect();
}
}
至此,Android MQTT通信功能已经可以实现了,然后关于MainActivity中的方法我就不一一的讲解了,网上关于Android MQTT通信的原理以及更加详细的讲解数不胜数,我相信大家也都不厌其烦了,起码我是那样的,各种各样的大差不差的讲解,但是偏偏代码不能用,或者是过时了,好像自己学习了MQTT,但是关键的自己按照别人说的就是调不通。所以我写博客的目的就是整理出来最简单的,不说多全面多好用,就起码要能用,也算是给大家做一点微不足道的贡献。