1. 程式人生 > >Android中MQTT的簡單實現(只是連線到伺服器,未實現傳送、接受資訊)

Android中MQTT的簡單實現(只是連線到伺服器,未實現傳送、接受資訊)

1.新增mqtt包到gradle.build

a.在project的gradle.build中新增地址(P:我下載的參考例子是不用新增的,但是我自己寫的時候不新增就編譯不過去)

allprojects {
    repositories {
        google()
        jcenter()
        maven {
            url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
        }
    }
}

b.在app的gradle.build中新增MQTT的依賴

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
    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.在AndroidMenifest.xml中新增許可權和service

(P:那個WAKE_LOCK,ACCESS_NETWORK_STATE許可權和service一定要新增,之前參考網上的例子寫的時候沒有注意到要新增service,啟動mqtt.connect一直返回連線失敗,列印失敗資訊報cannot start service org.eclipse.paho.android.service.MqttService,然後添加了service又報WAKE_LOCK,ACCESS_NETWORK_STATE許可權安全錯誤,所以一定要注意新增它們)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.chenss.baselib">

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:name=".MyApplication"
        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>
    </application>

 3.佈局

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">
    <android.support.v7.widget.AppCompatEditText
        android:id="@+id/host"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入URl:格式tcp://ip地址"
        android:layout_marginTop="5dp"
        android:padding="20dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <android.support.v7.widget.AppCompatEditText
        android:id="@+id/port"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入埠號"
        android:layout_marginTop="5dp"
        android:padding="20dp"
        app:layout_constraintTop_toBottomOf="@+id/host"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"/>

    <android.support.v7.widget.AppCompatEditText
        android:id="@+id/clientId"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入clientId"
        android:layout_marginTop="5dp"
        android:padding="20dp"
        app:layout_constraintTop_toBottomOf="@+id/port"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"/>
    <android.support.v7.widget.AppCompatEditText
        android:id="@+id/userName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入使用者名稱"
        android:layout_marginTop="5dp"
        android:padding="20dp"
        app:layout_constraintTop_toBottomOf="@+id/clientId"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"/>

    <android.support.v7.widget.AppCompatEditText
        android:id="@+id/passWord"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入密碼"
        android:layout_marginTop="5dp"
        android:padding="20dp"
        app:layout_constraintTop_toBottomOf="@+id/userName"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"/>

    <Button
        android:id="@+id/connect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="連線"
        android:background="@color/colorAccent"
        android:layout_marginTop="5dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/passWord" />

</android.support.constraint.ConstraintLayout>

4 .實現程式碼:MainActivity

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @BindView(R.id.host)
    AppCompatEditText host;

    @BindView(R.id.port)
    AppCompatEditText port;

    @BindView(R.id.clientId)
    AppCompatEditText clientId;


    @BindView(R.id.connect)
    Button connect;

    @BindView(R.id.userName)
    AppCompatEditText userName;
    @BindView(R.id.passWord)
    AppCompatEditText passWord;

    private MqttAndroidClient mClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(MainActivity.this);
    }

    @OnClick(R.id.connect)
    public void onViewClicked() {
        if (check()) {
           connect();
        }
    }

    private void connect() {
       String serUrl =getServerURI(host.getText().toString(), port.getText().toString());
        connect.setText("正在連線");
        //serUrl為主機名,clientid即連線MQTT的客戶端ID,一般以客戶端唯一識別符號表示,MemoryPersistence設定clientid的儲存形式,預設為以記憶體儲存
        mClient = new MqttAndroidClient(MyApplication.getContext(), serUrl, clientId.getText().toString(),new MemoryPersistence());
        //設定回撥
        mClient.setCallback(mqttCallbackExtended);

        try {
            mClient.connect(getMqttConnectOptions(), "Connect", iMqttActionListener);
        } catch (MqttException ex) {
            ex.printStackTrace();
        }
    }


    private boolean check() {
        if (TextUtils.isEmpty(host.getText().toString())) {
            Toast.makeText(MainActivity.this, "host不能為空", Toast.LENGTH_SHORT).show();
            return false;
        }
        if (TextUtils.isEmpty(port.getText().toString())) {
            Toast.makeText(MainActivity.this, "port不能為空", Toast.LENGTH_SHORT).show();
            return false;
        }
        if (TextUtils.isEmpty(clientId.getText().toString())) {
            Toast.makeText(MainActivity.this, "clientIds不能為空", Toast.LENGTH_SHORT).show();
            return false;
        }
        if (TextUtils.isEmpty(userName.getText().toString())) {
            Toast.makeText(MainActivity.this, "userName不能為空", Toast.LENGTH_SHORT).show();
            return false;
        }
        if (TextUtils.isEmpty(passWord.getText().toString())) {
            Toast.makeText(MainActivity.this, "passWord不能為空", Toast.LENGTH_SHORT).show();
            return false;
        }
        return true;
    }

    private MqttCallbackExtended mqttCallbackExtended = new MqttCallbackExtended() {
        @Override
        public void connectComplete(boolean reconnect, String serverURI) {
            //連線丟失後,一般在這裡面進行重連
            if (reconnect) {
                Log.e(TAG, "Reconnected to : " + serverURI);
                connect.setText("連線成功");
            } else {
                Log.e(TAG, "Connected to: " + serverURI);
                connect.setText("連線成功");
            }
        }

        @Override
        public void connectionLost(Throwable cause) {
            Log.e(TAG, "The Connection was lost.");
        }

        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
            //subscribe後得到的訊息會執行到這裡面
            Log.e(TAG, "Incoming message: " + new String(message.getPayload()));
        }

        @Override
        public void deliveryComplete(IMqttDeliveryToken token) {
            //publish後會執行到這裡
        }
    };

    private IMqttActionListener iMqttActionListener = new IMqttActionListener() {
        @Override
        public void onSuccess(IMqttToken asyncActionToken) {
            Log.e(TAG, "onSuccess!");
            connect.setText("連線成功");
        }

        @Override
        public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
            Log.e(TAG, "exception: " + exception.getMessage());
            connect.setText("連線失敗");
        }
    };

    public String getServerURI(String host,String port) {
        return "tcp://" + host + ":" + port;
    }

    //MQTT的連線設定
    public MqttConnectOptions getMqttConnectOptions() {
        MqttConnectOptions options = new MqttConnectOptions();
        //設定是否清空session,這裡如果設定為false表示伺服器會保留客戶端的連線記錄,這裡設定為true表示每次連線到伺服器都以新的身份連線
        options.setCleanSession(true);
        // 設定超時時間 單位為秒
        options.setConnectionTimeout(10);
        // 設定會話心跳時間 單位為秒 伺服器會每隔1.5*20秒的時間向客戶端傳送個訊息判斷客戶端是否線上,但這個方法並沒有重連的機制
        options.setKeepAliveInterval(20);
        //設定連線的使用者名稱
        options.setUserName(userName.getText().toString().trim());
        //設定連線的密碼
        options.setPassword(passWord.getText().toString().trim().toCharArray());
        return options;
    }

    @Override
    protected void onDestroy() {
        try {
            if (mClient!=null&&mClient.isConnected()){
                mClient.disconnect();
            }
        } catch (MqttException e) {
            e.printStackTrace();
        }
        super.onDestroy();
    }
}