Android/MQTT

[Android] MQTT + MQTTX 사용해보기

O_Gyong 2022. 12. 6.

안드로이드에서 MQTT 통신을 하는 방법은 아래와 같다.

1. org.eclipse.paho:org.eclipse.paho.client.mqttv3에서 제공하는 MqttClient 클래스 사용
2. org.eclipse.paho:org.eclipse.paho.android.service에서 제공하는 MqttAndroidClient 클래스 사용
3. com.github.hannesa2:paho.mqtt.android에서 제공하는 MqttAndroidClient  클래스 사용

eclipse에서 제공하는 1, 2번을 사용할 경우 SSL 인증서를 인증하는 부분에서 문제가 발생한다.

해당 라이브러리는 업데이트가 진행되지 않아 Android 12 버전에서 오류가 뜬다.

이런 이유로 3번을 사용해서 MQTT 통신을 했다.

(SSL 인증을 하는 것이 아니라면 eclipse에서 제공하는 라이브러리를 사용해도 된다.)

 

그리고 MQTTX 툴을 사용하여 통신이 되는 과정을 확인했다.

 

MQTT X: Cross-platform MQTT 5.0 Desktop Client

MQTT X: A cross-platform MQTT 5.0 desktop client open-sourced by EMQ, which can run on macOS, Linux and Windows, and supports formatting MQTT payload.

mqttx.app

 

예제로 브로커에 보낸 메시지와 브로커에서 오는 메시지를 TextView에 보여주려고 한다.


MQTT 통신 예제

1. hannesa2님의 github에서 라이브러리 다운로드
2. 외부 라이브러리 프로젝트에 등록하기
3. build.gradle과 settings.gradle 업데이트
4. MQTT 통신 코드 작성

라이브러리 다운로드

 

GitHub - hannesa2/paho.mqtt.android: Kotlin MQTT Android with almost all pull requests from upstream

Kotlin MQTT Android with almost all pull requests from upstream - GitHub - hannesa2/paho.mqtt.android: Kotlin MQTT Android with almost all pull requests from upstream

github.com

위 링크는 hannesa2님의 'paho.mqtt.android' 레포지토리이다.

해당 페이지에서 우측에 있는 Releases를 클릭하면 라이브러리를 다운로드할 수 있다.

나는 3.5.1 버전을 다운로드했다.


외부 라이브러리 등록

안드로이드 스튜디오의 Project 탭에서 보기 방식을 Project로 변경한다.

app 폴더 안에 libs 폴더가 있는데, 해당 폴더에 다운로드 한 라이브러리(aar 파일)를 넣어준다.


gradle 업데이트

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

우선 settings.gradle에 maven { url 'https://jitpack.io' } 를 추가하고 sync를 한다.


android {
    compileSdk 33

    defaultConfig {
        targetSdk 33
    }
}
// MQTT eclipse
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'

// MQTT hannesa2
implementation 'com.github.hannesa2:paho.mqtt.android:3.5.1'

다음으로 sdk 버전을 33으로 업데이트 및 의존성을 추가하고 sync를 한다.


MQTT 통신 작업

class MainActivity : AppCompatActivity() {
    private val serverUri:String = "tcp://ip 입력:1883"  //서버 IP
    private val topic:String = "o_gyong test"  // 토픽

    private var sendText = ""
    private var receiveText = ""

    private lateinit var mBinding: ActivityMainBinding

    // Mqtt 방식의 통신을 지원하는 클래스, MqttAndroidClient 객체 생성
    private lateinit var mqttClient: MqttAndroidClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mBinding.root)

        // MqttAndroidClient 초기화
        // → 인자 값으로 Act의 AUTO_ACT와 MANUAL_ACK이 있는데 정확한 사용법을 모르겠음..
        // →→ 기본적으로 AUTO_ACT로 동작함 : 메시지가 반환되면 프로세스에 메시지가 도착했다고 즉시 알림
        mqttClient = MqttAndroidClient(this, serverUri, MqttClient.generateClientId())

        // Mqtt 서버와 연결
        // MqttConnectOptions는 Mqtt의 Client가 서버에 연결하는 방법을 제어하는 클래스
        // 연결 결과 콜백 → callbackConnectResult
        mqttClient.connect(MqttConnectOptions(), null, callbackConnectResult)
    }


    /**
     * connect 결과 처리
     */
    private var callbackConnectResult = object : IMqttActionListener {
        override fun onSuccess(asyncActionToken: IMqttToken?) {
            println("성공 $asyncActionToken")
            // 연결에 성공하면 해당 토픽 구독
            mqttClient.subscribe(topic, 1)
            mqttCallBack()
            sendMessageMqtt()
        }

        override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
            println("실패 $exception")
        }
    }


    /**
     * 메시지 전송
     */
    private fun sendMessageMqtt() {
        mBinding.buttonSend.setOnClickListener {
            mqttClient.publish(topic, MqttMessage(mBinding.etSend.text.toString().toByteArray())) // 메세지 전송
            sendText = sendText + mBinding.etSend.text.toString() + "\n"
            mBinding.tvSend.text = sendText
        }
    }


    /**
     * 메시지 상태 콜백
     */
    private fun mqttCallBack() {
        // 콜백 설정
        mqttClient.setCallback(object : MqttCallback {
            // 연결이 끊겼을 경우
            override fun connectionLost(p0: Throwable?) {
                println("연결 끊어짐")
            }

            // 메세지가 도착했을 때
            override fun messageArrived(topic: String?, message: MqttMessage?) {
                println("topic $topic")
                println("message $message")
                receiveText = receiveText + message.toString() + "\n\n"
                mBinding.tvReceive.text = receiveText
            }

            // 메시지 전송이 성공했을 때
            override fun deliveryComplete(p0: IMqttDeliveryToken?) {
                println("메세지 전송 성공")
            }
        })
    }
}

전체 코드는 위와 같다.


MqttAndroidClient 초기화  및 연결
// MqttAndroidClient 초기화
// → 인자 값으로 Act의 AUTO_ACT와 MANUAL_ACK이 있는데 정확한 사용법을 모르겠음..
// →→ 기본적으로 AUTO_ACT로 동작함 : 메시지가 반환되면 프로세스에 메시지가 도착했다고 즉시 알림
mqttClient = MqttAndroidClient(this, serverUri, MqttClient.generateClientId())

// Mqtt 서버와 연결
// MqttConnectOptions는 Mqtt의 Client가 서버에 연결하는 방법을 제어하는 클래스
// 연결 결과 콜백 → callbackConnectResult
mqttClient.connect(MqttConnectOptions(), null, callbackConnectResult)

MqttAndroidClient는 Mqtt 방식의 통신을 지원하는 클래스이다.(hannesa2)

MqttConnectOptions는 Mqtt의 Client가 서버에 연결하는 방법을 제어하는 클래스이다.(eclipse)

 

참고로 IP 값은 MQTTX에서 아래 값을 넣어줬다.


connect 결과 처리
/**
 * connect 결과 처리
 */
private var callbackConnectResult = object : IMqttActionListener {
    override fun onSuccess(asyncActionToken: IMqttToken?) {
        println("성공 $asyncActionToken")
        // 연결에 성공하면 해당 토픽 구독
        mqttClient.subscribe(topic, 1)
        mqttCallBack()
        sendMessageMqtt()
    }

    override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
        println("실패 $exception")
    }
}

subscribe의 경우 connect 결과 콜백에서 호출하지 않으면  java.lang.NullPointerException이 발생할 수 있다.

Client가 연결되는 것이 성공했을 때 subscribe를 호출한다.

 

그리고 Client가 연결되는 즉시 메시지를 수신할 수 있도록 메시지 상태 콜백을 호출한다.


메시지 상태 콜백·전송 메서드 작성
/**
 * 메시지 전송
 */
private fun sendMessageMqtt() {
    mBinding.buttonSend.setOnClickListener {
        mqttClient.publish(topic, MqttMessage(mBinding.etSend.text.toString().toByteArray())) // 메세지 전송
        sendText = sendText + mBinding.etSend.text.toString() + "\n"
        mBinding.tvSend.text = sendText
    }
}


/**
 * 메시지 상태 콜백
 */
private fun mqttCallBack() {
    // 콜백 설정
    mqttClient.setCallback(object : MqttCallback {
        // 연결이 끊겼을 경우
        override fun connectionLost(p0: Throwable?) {
            println("연결 끊어짐")
        }

        // 메세지가 도착했을 때
        override fun messageArrived(topic: String?, message: MqttMessage?) {
            println("topic $topic")
            println("message $message")
            receiveText = receiveText + message.toString() + "\n\n"
            mBinding.tvReceive.text = receiveText
        }

        // 메시지 전송이 성공했을 때
        override fun deliveryComplete(p0: IMqttDeliveryToken?) {
            println("메세지 전송 성공")
        }
    })
}

 

전체 코드

'Android > MQTT' 카테고리의 다른 글

[Android] MQTT + SSL + MQTTX 사용해보기  (0) 2022.12.09

댓글