这里写目录标题
- 关于MQTT协议
- Android端实现:
- 1.在module中的build.gradle中添加以下依赖
- 2.在AndroidManifest.xml中添加权限
- 3.在 AndroidManifest.xml 注册Service
- 4.创建TqMqttService服务
- 5.Mqtt消息回调
- 6.创建TimingService服务
- 7.创建MQttMessage实体类,用于传送主题消息
- 8.开启服务
关于MQTT协议
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议。它是一种发布/订阅,极其简单和轻量级的消息传递协议,专为受限设备和低带宽,高延迟或不可靠的网络而设计。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境。相对于XMPP,MQTT更加轻量级,并且占用的宽带低。
MQTT协议有以下特点:
- 使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。
- 对负载内容屏蔽的消息传输。
- 使用 TCP/IP 提供网络连接。
- 有三种消息发布服务质量:
- qos为0:“至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
- qos为1:“至少一次”,确保消息到达,但消息重复可能会发生。这一级别可用于如下情况,你需要获得每一条消息,并且消息重复发送对你的使用场景无影响。
- qos为2:“只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。
- 小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量。
使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制。
Android端实现:
1.在module中的build.gradle中添加以下依赖
dependencies {
/**
* Gson解析
*/
api 'com.google.code.gson:gson:2.8.2'
/**
* MQTT
*/
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.0'
}
2.在AndroidManifest.xml中添加权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
3.在 AndroidManifest.xml 注册Service
<service android:name="org.eclipse.paho.android.service.MqttService"/>
<service android:name=".utils.TqMqttService"/>
<service android:name=".utils.TimingService"/>
4.创建TqMqttService服务
public class TqMqttService extends Service{
public static final String TAG = TqMqttService.class.getSimpleName();
public String HOST = "tcp://192.168.0.000:1883";
public String nameUser = "nameUser";
public String passWord = "passWord";
private static int Qos = 2;
private static MqttAndroidClient mqttAndroidClient;
public String CLIENTID = String.valueOf(new Date().getTime());//客户端标识(唯一性)
private MqttConnectOptions mMqttConnectOptions;
//将全部订阅接口存入数组
private String[] response = {
ConfigPort.RESPONSE_DEV_STATUS,
ConfigPort.RESPONSE_POWER_STATUS,
ConfigPort.RESPONSE_GET_ETHCFG,
ConfigPort.RESPONSE_SET_ETHCFG
};
//MQTT是否连接成功的监听
private IMqttActionListener iMqttActionListener = new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken arg0) {
Log.i(TAG, "Mqtt连接成功");
try {
//可先将订阅接口全部订阅,后期只需要进行主题消息发送
for (int i = 0; i < response.length; i++) {
mqttAndroidClient.subscribe(response[i], Qos);
}
} catch (MqttException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(IMqttToken arg0, Throwable arg1) {
arg1.printStackTrace();
Log.i(TAG, "Mqtt连接失败!");
doClientConnection();//连接失败,重连(可关闭服务器进行模拟)
}
};
//订阅主题的回调
private MqttCallback mqttCallback = new MqttCallback() {
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
String result = new String(message.getPayload());
Log.i(TAG, "Mqtt收到消息: " + result);
//收到消息后进行响应
sMqttData.onResult(result);
}
@Override
public void deliveryComplete(IMqttDeliveryToken arg0) {
Log.i(TAG, arg0.isComplete() ? "消息发送成功" : "消息发送失败");
}
@Override
public void connectionLost(Throwable arg0) {
Log.i(TAG, "连接断开 ");
Toast.makeText(getApplicationContext(), "Mqtt连接断开,正在重连!", Toast.LENGTH_LONG).show();
doClientConnection();//连接断开,重连
}
};
@Override
public void onCreate() {
super.onCreate();
init();
}
/**
* 开启服务
*/
public static void startService(Context mContext) {
mContext.startService(new Intent(mContext, TqMqttService.class));
}
/**
* 发布 (向服务器发送消息)
*
* @param topic 主题
* @param message 消息
*/
public static void publish(String topic, String message) {
Integer qos = Qos;
Boolean retained = false;
try {
//参数分别为:主题、消息的字节数组、服务质量、是否在服务器保留断开连接后的最后一条消息
if (mqttAndroidClient != null) {
mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
}
} catch (MqttException e) {
e.printStackTrace();
}
}
/**
* 初始化
*/
private void init() {
String serverURI = HOST; //服务器地址(协议+地址+端口号)
mqttAndroidClient = new MqttAndroidClient(this, serverURI, CLIENTID);
mqttAndroidClient.setCallback(mqttCallback); //设置监听订阅消息的回调
mMqttConnectOptions = new MqttConnectOptions();
mMqttConnectOptions.setCleanSession(true); //设置是否清除缓存
mMqttConnectOptions.setConnectionTimeout(10); //设置超时时间,单位:秒
mMqttConnectOptions.setKeepAliveInterval(10); //设置心跳包发送间隔,单位:秒
mMqttConnectOptions.setAutomaticReconnect(true); // 设置自动重连
mMqttConnectOptions.setUserName(nameUser); //设置用户名
mMqttConnectOptions.setPassword(passWord.toCharArray()); //设置密码
if (mqttAndroidClient != null) {
doClientConnection();
}
}
/**
* 注册订阅
* 订阅就是被动接受(接受另一个设备发送的topic)
*/
public static void subscribes(String response) {
try {
Integer qos = 2;
//订阅查询的主题、服务质量
Log.i(TAG, "订阅:"+ response);
if (mqttAndroidClient != null) {
mqttAndroidClient.subscribe(response, qos.intValue());
}
} catch (MqttException e) {
e.printStackTrace();
}
}
/**
* 连接MQTT服务器
*/
private void doClientConnection() {
if (!mqttAndroidClient.isConnected() && isConnectIsNomarl()) {
try {
mqttAndroidClient.connect(mMqttConnectOptions, null, iMqttActionListener);
} catch (MqttException e) {
e.printStackTrace();
}
}
}
/**
* 判断网络是否连接
*/
private boolean isConnectIsNomarl() {
ConnectivityManager connectivityManager = (ConnectivityManager) this.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = connectivityManager.getActiveNetworkInfo();
if (info != null && info.isAvailable()) {
String name = info.getTypeName();
Log.i(TAG, "当前网络名称:" + name);
return true;
} else {
Log.i(TAG, "没有可用网络");
/*没有可用网络的时候,延迟3秒再尝试重连*/
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
doClientConnection();
}
}, 3000);
return false;
}
}
public static boolean isConnected() {
if (mqttAndroidClient != null) {
return mqttAndroidClient.isConnected();
}
return false;
}
@Override
public void onDestroy() {
try {
mqttAndroidClient.disconnect(); //断开连接
} catch (MqttException e) {
e.printStackTrace();
}
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public static MqttData sMqttData;
public static void setMqttData(MqttData mqttData) {
sMqttData = mqttData;
}
}
5.Mqtt消息回调
public interface MqttData {
void onResult(String json);
}
6.创建TimingService服务
public class TimingService extends Service {
private String TAG = TimingService.class.getSimpleName();
private int TIME_INTERVAL = 1000 * 60 * 1;//设置三分钟执行一次
private PendingIntent mPendingIntent;
private AlarmManager mAlarmManager;
public static final String TEST_ACTION = "timinService";
@Override
public void onCreate() {
super.onCreate();
IntentFilter intentFilter = new IntentFilter(TEST_ACTION);
registerReceiver(receiver, intentFilter);
mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent();
intent.setAction(TEST_ACTION);
mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//6.0低电量模式需要使用该方法触发定时任务
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), mPendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//4.4以上 需要使用该方法精确执行时间
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), mPendingIntent);
} else {//4.4一下 使用老方法
mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, mPendingIntent);
}
}
public static void startService(Context mContext) {
mContext.startService(new Intent(mContext, TimingService.class));
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TEST_ACTION.equals(action)) {
Log.i(TAG, "定时任务执行");
sendReadStatusOrder();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, mPendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, mPendingIntent);
}
}
}
};
/**
* 发送指令读取
*/
public void sendReadStatusOrder() {
try {
for (int i = 3; i < 11; i++) {
int timeNumber = 1000 * (i - 3);
Message msg = new Message();
msg.what = 0;
msg.obj = "指令";
sendHandler.sendMessageDelayed(msg, timeNumber);
}
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, Log.getStackTraceString(e));
}
}
Handler sendHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String result = msg.obj.toString();
Log.i(TAG, "Handler result: " + result);
}
};
}
7.创建MQttMessage实体类,用于传送主题消息
public class MQttMessage {
/*{
"token": 123,
"timestamp": "2023-4-21-16:07:00",
}*/
private int token;
private String timestamp;
public MQttMessage(int token, String timestamp) {
this.token = token;
this.timestamp = timestamp;
}
public int getToken() {
return token;
}
public void setToken(int token) {
this.token = token;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}
8.开启服务
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv_temp)
TextView tvTemp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TqMqttService.startService(mContext);//开启服务
TimingService.startService(mContext);
//点击获取消息
tvTemp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
TqMqttService.publish(ConfigPort.REQUEST_DEV_STATUS,
new Gson().toJson(new MQttMessage(2, DataUtils.getCurrentTime(0))));
//消息回调
TqMqttService.setMqttData(json -> {
//将json数据通过Gson存入DevStatusBean
DevStatusBean mStatusBean = new Gson().fromJson(json, DevStatusBean.class);
});
}
});
}
}