MQTT服務端python和移動終端Android通信,實現無線測試 1. 服務器的搭建和調試 2. 整體設計思路 3. Android端代碼 4. 最終的效果

1. 服務器的搭建和調試

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章