1. 服务器的搭建和调试
- 请参考上一篇文章MQTT 服务器搭建,python控制消息发布和接收
2. 整体设计思路
- Android端连接服务器,连上之后发送相同主题的消息,
- PC端的python可以接收到消息,根据接收到的的消息发送反馈消息,
- Android收到消息作出相应操作,达到Android手机无线测试的效果
3. Android端代码
- 在gradle中的dependencies里边添加依赖,导入jar包
compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
- AndroidManifest.xml中添加权限,添加mqtt services
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.intent.action.BOOT_COMPLETED" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="org.eclipse.paho.android.service.MqttService" />
<service android:name=".StatusSendServices"/>
</application>
- MQTTUtility帮助类,
发布消息,主要是publish这个方法
订阅的主题回调是在mqttCallback ,接收到消息后可以在messageArrived方法体里边做操作手机部分的代码、
注意的是这个工具类是单例模式
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.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class MqttUtil {
private final String TAG = "mqtt";
private static MqttUtil mqttUtil;
private Context context;
private MqttAndroidClient mqttAndroidClient;
private MqttConnectOptions mMqttConnectOptions;
private boolean isConnectSuccess = false;
//MQTT相关配置
private final String CLIENTID = "AndroidUser";
public String HOST = "tcp://填写搭建服务器的IP地址:1883";//服务器地址(协议+地址+端口号)
public String USERNAME = "root";//用户名
public String PASSWORD = "root";//密码
public static String PUBLISH_TOPIC = "testPublish";//发布主题
public static String RESPONSE_TOPIC = "testResponse";//订阅主题
/**
* QUALITY_OF_SERVICE
* 至多一次,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
* 至少一次,确保消息到达,但消息重复可能会发生。
* 只有一次,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果
*/
private final int QUALITY_OF_SERVICE = 0;//服务质量,0最多一次,1最少一次,2只一次
private final int HAND_RECONNECT = 1;//重连hand
private final int RECONNECT_TIME_CONFIG = 10 * 1000;//重连时间间隔为10秒
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case HAND_RECONNECT:
if (!isConnectSuccess)
doClientConnection();//连接失败,重连(可关闭服务器进行模拟)
break;
}
}
};
//MQTT是否连接成功的监听
private IMqttActionListener iMqttActionListener = new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken arg0) {
Log.i(TAG, "连接成功 ");
isConnectSuccess = true;
try {
mqttAndroidClient.subscribe(PUBLISH_TOPIC, QUALITY_OF_SERVICE);//订阅主题,参数:主题、服务质量
} catch (MqttException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(IMqttToken arg0, Throwable arg1) {
arg1.printStackTrace();
Log.i(TAG, "onFailure 连接失败:" + arg1.getMessage());
isConnectSuccess = false;
handler.sendEmptyMessageDelayed(HAND_RECONNECT, RECONNECT_TIME_CONFIG);
}
};
//订阅主题的回调
private MqttCallback mqttCallback = new MqttCallback() {
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
Log.i(TAG, "收到消息: " + new String(message.getPayload()) + "\tToString:" + message.toString());
//收到其他客户端的消息后,响应给对方告知消息已到达或者消息有问题等
//response("message arrived:"+message);
}
@Override
public void deliveryComplete(IMqttDeliveryToken arg0) {
Log.i(TAG, "deliveryComplete");
}
@Override
public void connectionLost(Throwable arg0) {
Log.i(TAG, "连接断开");
doClientConnection();//连接断开,重连
}
};
//单例模式
public static MqttUtil getInstance(Context context) {
if (mqttUtil == null) {
mqttUtil = new MqttUtil(context);
}
return mqttUtil;
}
private MqttUtil(Context context) {
this.context = context;
initMqtt();
}
private void initMqtt() {
String serverURI = HOST; //服务器地址(协议+地址+端口号)
mqttAndroidClient = new MqttAndroidClient(context, serverURI, CLIENTID);
mqttAndroidClient.setCallback(mqttCallback); //设置订阅消息的回调
mMqttConnectOptions = new MqttConnectOptions();
mMqttConnectOptions.setCleanSession(true); //设置是否清除缓存
mMqttConnectOptions.setConnectionTimeout(10); //设置超时时间,单位:秒
mMqttConnectOptions.setKeepAliveInterval(20); //设置心跳包发送间隔,单位:秒
// mMqttConnectOptions.setUserName(USERNAME); //设置用户名
// mMqttConnectOptions.setPassword(PASSWORD.toCharArray()); //设置密码
// last will message
boolean doConnect = true;
String message = "{\"terminal_uid\":\"" + CLIENTID + "\"}";
String topic = PUBLISH_TOPIC;
Integer qos = QUALITY_OF_SERVICE;
Boolean retained = false;
if ((!message.equals("")) || (!topic.equals(""))) {
try {
mMqttConnectOptions.setWill(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
} catch (Exception e) {
Log.i(TAG, "setWill Exception Occured:" + e.getMessage(), e);
doConnect = false;
iMqttActionListener.onFailure(null, e);
}
}
if (doConnect) {
Log.i(TAG, "mMqttConnectOptions.setWill Success");
doClientConnection();
}
}
/**
* 连接MQTT服务器
*/
private void doClientConnection() {
Log.i(TAG, "是否链接成功:" + mqttAndroidClient.isConnected());
if (!mqttAndroidClient.isConnected()) {
try {
mqttAndroidClient.connect(mMqttConnectOptions, null, iMqttActionListener);
} catch (MqttException e) {
Log.i(TAG, "doClientConnection:" + e.getMessage());
e.printStackTrace();
}
}
}
/**
* 发布消息
*
* @param message 消息
*/
public void publish(String message) {
String topic = PUBLISH_TOPIC;
Integer qos = QUALITY_OF_SERVICE;
Boolean retained = false;
try {
if (mqttAndroidClient != null && mqttAndroidClient.isConnected()) {
//参数分别为:主题、消息的字节数组、服务质量、是否在服务器保留断开连接后的最后一条消息
mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
} else {
Log.i(TAG, "mqttAndroidClient is Null");
}
} catch (MqttException e) {
Log.i(TAG, "publish MqttException:" + e.getMessage());
e.printStackTrace();
}
}
public void response(String message) {
String topic = RESPONSE_TOPIC;
Integer qos = QUALITY_OF_SERVICE;
Boolean retained = false;
try {
//参数分别为:主题、消息的字节数组、服务质量、是否在服务器保留断开连接后的最后一条消息
mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
} catch (MqttException e) {
Log.i(TAG, "publish:" + e.getMessage());
e.printStackTrace();
}
}
//断开链接
public void disconnect() {
try {
if (mqttAndroidClient != null)
mqttAndroidClient.disconnect();
} catch (MqttException e) {
e.printStackTrace();
}
}
}
- MainActivity中创建一个按钮,点击按钮启动一个services,然后在services中触发发布消息
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//连接服务器
MqttUtil.getInstance(this);
Button sendButton = (Button) findViewById(R.id.sendButton);
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(MainActivity.this, StatusSendServices.class);
startService(intent);
}
}).start();
}
});
}
}
- StatusSendServices中获取手机中的一些信息,然后发布
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
public class StatusSendServices extends Service {
private Context context;
private MqttUtil mqttUtil;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
context = this;
mqttUtil = MqttUtil.getInstance(context);
//获取手机的串号 发布,HelperUtility.getCommandResult这个方法就不罗列了,可以根据自己需求调换
String devicesID = HelperUtility.getCommandResult("getprop ro.boot.serialno");
mqttUtil.publish(devicesID);
return Service.START_NOT_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
4. 最终的效果
参考
https://blog.csdn.net/yingtian648/article/details/99008119