2023.1.1
使用 new UI
在settings.gradle.kts中添加如下语句
maven { url = uri ("https://jitpack.io/")}
下图为上下文(不同版本该语句格式可能不同)
点击提示 sync now 或者点击该按钮
在build.gradle.kts中添加如下语句
//MQTT
implementation("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5")
implementation("org.eclipse.paho:org.eclipse.paho.android.service:1.1.1")
下图为上下文(不同版本该语句格式可能不同)
<!-- 网络权限-->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 网络状态权限-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 保持唤醒状态-->
<uses-permission android:name="android.permission.WAKE_LOCK" />
下图为上下文
<!-- Mqtt Service -->
<service android:name="org.eclipse.paho.android.service.MqttService">
</service>
下图为上下文
记得在清单文件中添加对应的activity
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:layout_editor_absoluteY="10dp">
<TextView
android:id="@+id/tv_mqtt_server"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/mqtt_server"
android:textSize="30sp"
android:layout_marginBottom="30dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="120dp"
android:layout_height="50dp"
android:text="@string/address"
android:gravity="right"
android:textSize="30sp"
tools:ignore="RtlHardcoded" />
<EditText
android:id="@+id/et_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_address"
android:textSize="25sp"
android:inputType="text" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="120dp"
android:layout_height="50dp"
android:text="@string/port"
android:gravity="right"
android:textSize="30sp" />
<EditText
android:id="@+id/et_port"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_port"
android:textSize="25sp"
android:inputType="text" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="120dp"
android:layout_height="50dp"
android:text="@string/client_id"
android:gravity="right"
android:textSize="30sp" />
<EditText
android:id="@+id/et_client_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_client_id"
android:textSize="25sp"
android:inputType="text"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="120dp"
android:layout_height="50dp"
android:text="@string/keep_alive"
android:gravity="right"
android:textSize="30sp" />
<EditText
android:id="@+id/et_keep_alive"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_keep_alive"
android:textSize="25sp"
android:inputType="number"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="120dp"
android:layout_height="50dp"
android:text="@string/username"
android:gravity="right"
android:textSize="30sp" />
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_username"
android:textSize="25sp"
android:inputType="text"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginBottom="30dp">
<TextView
android:layout_width="120dp"
android:layout_height="50dp"
android:text="@string/password"
android:gravity="right"
android:textSize="30sp" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_password"
android:inputType="textPassword"
android:textSize="25sp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/btn_connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/connect"
android:textSize="25sp"
android:layout_marginEnd="20dp"/>
<Button
android:id="@+id/btn_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/test"
android:textSize="25sp"
android:layout_marginStart="20dp"/>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
下图为预览图
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/subscribe"
android:textSize="30sp"
android:layout_marginBottom="20dp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="20dp"
>
<TextView
android:layout_width="110dp"
android:layout_height="wrap_content"
android:text="@string/topic"
android:textSize="25sp"
android:gravity="right"
tools:ignore="RtlHardcoded" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_topic"
android:hint="@string/hint_topic"
android:textSize="25sp"
android:inputType="text"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="20dp"
>
<TextView
android:layout_width="110dp"
android:layout_height="wrap_content"
android:text="@string/qos"
android:textSize="25sp"
android:gravity="right"
tools:ignore="RtlHardcoded" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_qos"
android:hint="@string/hint_qos"
android:textSize="25sp"
android:inputType="number"
/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="250dp"
android:id="@+id/tv_message"
android:text="@string/tv_message"
android:textSize="25sp"
android:layout_marginBottom="20dp"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/subscribe"
android:id="@+id/btn_subscribe"
android:layout_marginBottom="20dp"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/publish"
android:id="@+id/btn_go_publish"
android:layout_marginBottom="20dp"
/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
下图为预览图
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/publish"
android:textSize="30sp"
android:layout_marginBottom="20dp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="20dp"
>
<TextView
android:layout_width="110dp"
android:layout_height="wrap_content"
android:text="@string/topic"
android:textSize="25sp"
android:gravity="right"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_ptopic"
android:hint="@string/hint_topic"
android:textSize="25sp"
android:inputType="text"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="20dp"
>
<TextView
android:layout_width="110dp"
android:layout_height="wrap_content"
android:text="@string/qos"
android:textSize="25sp"
android:gravity="right"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_pqos"
android:hint="@string/hint_qos"
android:textSize="25sp"
android:inputType="number"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="20dp"
>
<TextView
android:layout_width="110dp"
android:layout_height="wrap_content"
android:text="@string/retained"
android:textSize="25sp"
android:gravity="right"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_retained"
android:hint="@string/hint_retained"
android:textSize="25sp"
android:inputType="text"
/>
</LinearLayout>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_pmessage"
android:hint="@string/hint_message"
android:textSize="25sp"
android:inputType="text"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/publish"
android:id="@+id/btn_publish"
android:layout_marginBottom="20dp"
/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
预览图
package com.example.demo_mqttserver;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class MainActivity extends AppCompatActivity {
protected static MqttClient mqttClient;//客户端
private MqttConnectOptions options;//配置 保存控制客户端连接到服务器的方式的选项集。
private TextView tv_mqtt_server;
private EditText et_address,et_port,et_clientId,et_username,et_password,et_keep_alive;
private Button btn_connect,btn_test;
private String host,clientId,username,password,message;
private int keep_alive;
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initview();
btn_connect.setOnClickListener(v -> {
if (isOnline()){
if (isequals()) {
host = "tcp://" + et_address.getText().toString() + ":" + et_port.getText().toString();
clientId = et_clientId.getText().toString();
keep_alive = Integer.parseInt(et_keep_alive.getText().toString());
username = et_username.getText().toString();
password = et_password.getText().toString();
initMqtt(host, clientId, username, password, keep_alive);
connect_mqtt(true);
} else {
Snackbar.make(tv_mqtt_server, "请填入完整信息!", Snackbar.LENGTH_SHORT).show();
}
}else {
Snackbar.make(tv_mqtt_server, "请检查网络连接!", Snackbar.LENGTH_SHORT).show();
}
});
btn_test.setOnClickListener(v -> {
initMqtt("tcp://192.168.123.183:1883","app-0A:0B:0C","app-test","app123456",60);
connect_mqtt(true);
});
}
/**
* 初始化控件*/
public void initview(){
intent = new Intent(MainActivity.this,SubscribeActivity.class);
tv_mqtt_server = findViewById(R.id.tv_mqtt_server);
et_address = findViewById(R.id.et_address);
et_port = findViewById(R.id.et_port);
et_clientId = findViewById(R.id.et_client_id);
et_keep_alive= findViewById(R.id.et_keep_alive);
et_username = findViewById(R.id.et_username);
et_password = findViewById(R.id.et_password);
btn_connect = findViewById(R.id.btn_connect);
btn_test=findViewById(R.id.btn_test);
}
/**
* 判断输入框是否为空
* @return true不为空,false为空*/
private boolean isequals(){
return !et_address.getText().toString().equals("") && !et_port.getText().toString().equals("") && !et_clientId.getText().toString().equals("") && !et_username.getText().toString().equals("") && !et_password.getText().toString().equals("");
}
/**
* 初始化Mqtt
* @param host 服务器地址+端口号
* @param clientId 客户端ID
* @param username 用户名
* @param password 密码
* @param keep_alive 保持连接时间*/
private void initMqtt(String host,String clientId,String username,String password,int keep_alive){
try {
//(1)主机地址(2)客户端ID,一般以客户端唯一标识符(不能够和其它客户端重名)(3)最后一个参数是指数据保存在内存(具体保存什么数据,以后再说,其实现在我也不是很确定)
mqttClient = new MqttClient(host, clientId, new MemoryPersistence());
} catch (MqttException e) {
e.printStackTrace();
}
options = new MqttConnectOptions();//MQTT的连接设置
options.setCleanSession(true);//设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接
options.setUserName(username);//设置连接的用户名(自己的服务器没有设置用户名)
options.setPassword(password.toCharArray());//设置连接的密码(自己的服务器没有设置密码)
options.setKeepAliveInterval(keep_alive);
options.setConnectionTimeout(10);
mqttClient.setCallback(new MqttCallback() {
@Override//连接丢失后,会执行这里
public void connectionLost(Throwable throwable) {
}
@Override//获取的消息会执行这里--arg0是主题,arg1是消息
public void messageArrived(final String ssr, MqttMessage mqttMessage) throws Exception {
message = mqttMessage.toString();//消息
runOnUiThread(new Runnable() {//
public void run() {
Log.d("mqtt", "收到消息");
Log.d("主题", ssr);
Log.d("消息", message);
SubscribeActivity.tv_message.setText(message);
}
});
}
@Override//订阅主题后会执行到这里
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
});
}
/**
* 连接mqtt
* @param bloConn true连接,false断开连接*/
private void connect_mqtt(boolean bloConn) {
// 必须开启新的线程执行
new Thread(new Runnable() {
@Override
public void run() {
if (bloConn) {
try {
if (!mqttClient.isConnected() ) {
mqttClient.connect(options);//连接服务器,连接不上会阻塞在这
}
Snackbar.make(tv_mqtt_server, "连接成功!", Snackbar.LENGTH_SHORT).show();
startActivity(intent);
} catch (MqttException e) {
e.printStackTrace();
Snackbar.make(tv_mqtt_server, "连接失败!", Snackbar.LENGTH_SHORT).show();
}
} else {
try {
if (mqttClient.isConnected()) {
mqttClient.disconnect();
}
Snackbar.make(tv_mqtt_server, "断开成功!", Snackbar.LENGTH_SHORT).show();
} catch (MqttException e) {
e.printStackTrace();
Snackbar.make(tv_mqtt_server, "断开失败!", Snackbar.LENGTH_SHORT).show();
}
}
}
}).start();
}
/**
* 判断网络是否连接
* @return true为连接,false为未连接*/
public boolean isOnline() {
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
mqttClient.disconnect();
}catch (MqttException e) {
e.printStackTrace();
}
}
}
package com.example.demo_mqttserver;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.snackbar.Snackbar;
import org.eclipse.paho.client.mqttv3.MqttException;
public class SubscribeActivity extends AppCompatActivity {
protected static TextView tv_message;
private String topic;
private EditText et_topic, et_qos;
private int qos;
private Button btn_subscribe, btn_go_publish;
@Override
public void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_subscribe);
initview();
btn_subscribe.setOnClickListener(v -> {
if (isequals()){
topic = et_topic.getText().toString();
qos = Integer.parseInt(et_qos.getText().toString());
try {
MainActivity.mqttClient.subscribe(topic, qos);
} catch (MqttException e) {
e.printStackTrace();
Snackbar.make(tv_message, "订阅失败!", Snackbar.LENGTH_SHORT).show();
}
Snackbar.make(tv_message, "订阅成功", Snackbar.LENGTH_SHORT).show();
}else {
Snackbar.make(tv_message, "请填入完整信息!", Snackbar.LENGTH_SHORT).show();
}
});
btn_go_publish.setOnClickListener(v -> {
startActivity(new Intent(this, PublishActivity.class));
});
}
/**
* 初始化组件*/
public void initview() {
tv_message =findViewById(R.id.tv_message);
et_topic = findViewById(R.id.et_topic);
et_qos = findViewById(R.id.et_qos);
btn_subscribe=findViewById(R.id.btn_subscribe);
btn_go_publish = findViewById(R.id.btn_go_publish);
}
/**
* 判断输入框是否为空
* @return true:不为空 false:为空*/
private boolean isequals(){
return !et_topic.getText().toString().equals("")&&!et_qos.getText().toString().equals("");
}
}
package com.example.demo_mqttserver;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.snackbar.Snackbar;
import org.eclipse.paho.client.mqttv3.MqttException;
public class PublishActivity extends AppCompatActivity {
private EditText et_ptopic,et_pmsg,et_pqos,et_retained;
private Button btn_publish;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_publish);
initview();
btn_publish.setOnClickListener(v -> {
if (isequals()){
String topic;
byte []msg = et_pmsg.getText().toString().getBytes();
int qos;
boolean retained = false;
if(et_retained.getText().toString().equals("true"))retained=true;
else if (et_retained.getText().toString().equals("false")) retained=false;
topic = et_ptopic.getText().toString();
qos = Integer.parseInt(et_pqos.getText().toString());
try {
MainActivity.mqttClient.publish(topic,msg,qos,retained);
} catch (MqttException e) {
e.printStackTrace();
Snackbar.make(btn_publish, "发布!", Snackbar.LENGTH_SHORT).show();
}
Snackbar.make(btn_publish, "发布成功", Snackbar.LENGTH_SHORT).show();
}else {
Snackbar.make(btn_publish, "请填入完整信息!", Snackbar.LENGTH_SHORT).show();
}
});
}
private void initview()
{
et_ptopic = findViewById(R.id.et_ptopic);
et_pmsg = findViewById(R.id.et_pmessage);
et_pqos = findViewById(R.id.et_pqos);
et_retained = findViewById(R.id.et_retained);
btn_publish = findViewById(R.id.btn_publish);
}
/**
* 判断输入框是否为空
* @return true:不为空 false:为空*/
private boolean isequals(){
return !et_ptopic.getText().toString().equals("")&&!et_pqos.getText().toString().equals("")&&! et_pmsg.getText().toString().equals("");
}
}
<resources>
<string name="app_name">MQTT客户端</string>
<string name="disconnect">断开</string>
<string name="connect">连接</string>
<string name="hint_password">请输入密码</string>
<string name="password">密码</string>
<string name="hint_username">请输入用户名</string>
<string name="username">用户名</string>
<string name="hint_client_id">请输入客户端ID</string>
<string name="client_id">客户端id</string>
<string name="hint_address">请输入ip</string>
<string name="address">IP地址</string>
<string name="port">端口号</string>
<string name="hint_port">请输入端口号</string>
<string name="mqtt_server">MQTT 客户端</string>
<string name="keep_alive">心跳</string>
<string name="hint_keep_alive">请输入心跳</string>
<string name="test">Test</string>
<string name="tv_subscribe">主题订阅</string>
<string name="hint_topic">请输入主题名称</string>
<string name="subscribe">订阅</string>
<string name="tv_message">消息显示区</string>
<string name="topic">主题 </string>
<string name="qos">服务质量</string>
<string name="hint_qos">请输入服务质量</string>
<string name="publish">发布</string>
<string name="hint_message">请输入消息</string>
<string name="retained">保留</string>
<string name="hint_retained">true/false</string>
</resources>