First, library download

In making search paho. MQTT. C, or directly in the browser to input the project address, https://github.com/eclipse/paho.mqtt.c, you can choose to download the browser, can also use the git version tool to clone the project to the local.

Two, pretreatment

For C, C++ projects, there are no more than two ways to transplant, one is to compile into a static library or a dynamic library, one is to directly copy the source file to your project, but considering the cross-compilation environment is more difficult to build, so the temporary use is to copy the source file to transplant.

1. Create the AndroidC++ project

Make sure your Android Studio has the NDK environment configured, then create a C++ project.

2. Copy the source file

After creation, create a new MQTT directory under the CPP directory and copy all the contents of SRC under the paho.mqtt.c directory under the newly created MQTT folder, excluding the samples directory and the versionInfo.h.N file.

3. Write CmakeList. TXT

My current Android Studio version is above 3.2, using cmake to compile C++, according to cmakelist. TXT can be nested, modify the cmakelist. TXT file under MQTT directory, so that Android Studio can compile MQTT. The modified code is as follows:

Cmake_minimum_required (VERSION 3.4.1 track)set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") add_library( mqtt STATIC Base64.c Clients.c Heap.c LinkedList.c Log.c Messages.c MQTTAsync.c MQTTClient.c MQTTPacket.c  MQTTPacketOut.c MQTTPersistence.c MQTTPersistenceDefault.c MQTTProperties.c MQTTProtocolClient.c MQTTProtocolOut.c MQTTReasonCodes.c MQTTVersion.c OsWrapper.c SHA1.c Socket.c SocketBuffer.c SSLSocket.c StackTrace.c Thread.c Tree.c utf-8.c WebSocket.c )Copy the code

In the main cmakelist.txt file, you just need to import the child cmakelist.txt

add_subdirectory(src/main/cpp/mqtt)
Copy the code

At this point, the introduction of MQTT is basically completed, and the rebuild project is ok

3. Choose the development style that works for you

The C version of MQTT library has two sets of API implementation, one is synchronous, one is asynchronous, if you use synchronous mode to develop, then you have to worry about thread messaging, in Android you can think of C++ layer implementation of your own Handler mechanism. So we use an asynchronous API to implement the client.

Four, the main API introduction

int MQTTAsync_create(MQTTAsync* handle, const char* serverURI, const char* clientId, int persistence_type, Void * persistence_context);Copy the code

This function creates an MQTT client that connects to a specific server with a specific persistent storage. Handle -> Pointer to the MQTT client handle. The handle is populated with serverURI-> a null-terminated string that specifies the server to which the client will connect with the client reference returned successfully from the function. The format is protocol://host:port. The current protocol must be TCP or SSL, and host can be specified as an IP address or domain name. For example, to use the default MQTT port to connect to a server running on the local computer, specify TCP ://localhost:1883. The clientId -> client identifier (clientId) is a null-terminated UTF-8 encoded string that is passed to the client when it connects to the server. Persistence_type -> The persistent type used by the client. MQTTCLIENT_PERSISTENCE_NONE- Uses in-memory persistence. If the device or system on which the client is running fails or shuts down, the current state of any running messages is lost, and some messages may not even be delivered in QoS1 and QoS2; MQTTCLIENT_PERSISTENCE_DEFAULT- Uses the default persistence mechanism (file system). The state of the running message is kept in persistent storage to provide some protection against message loss in the event of an exit. MQTTCLIENT_PERSISTENCE_USER- Uses an application-specified persistence implementation. With this type, applications have control over the persistence mechanism and must implement the MQTTClient_persistence interface. Persistence_context -> If the application uses persistence MQTTCLIENT_PERSISTENCE_NONE, this parameter is not used and the value should be set to NULL. For MQTTCLIENT_PERSISTENCE_DEFAULT persistence, the location of the persistence directory should be set (if set to NULL, the working directory is used as the persistence directory). With MQTTCLIENT_PERSISTENCE_USER persistence, this parameter points to a valid MQTTClient_persistence structure.

int MQTTAsync_setCallbacks(MQTTAsync handle, void* context,
									MQTTAsync_connectionLost* cl,
									MQTTAsync_messageArrived* ma,
									MQTTAsync_deliveryComplete* dc);
Copy the code

This function creates a callback function for a particular client. If your client application does not use a specific callback function, set the relevant parameter to NULL. Call MQTTClient_setCallbacks () to put the client into multithreaded mode. Any necessary message confirmation and status communication is handled in the background without any intervention from the client application. Handle -> Pointer to the MQTT client handle. The handle is populated with a client reference successfully returned from the function context-> a pointer to any application-specific context. Context Pointers are passed to each callback function to provide access to context information in the callback. Cl -> pointer to the MQTTClient_connectionLost() callback function. If your application does not handle disconnections, you can set it to NULL. Ma -> pointer to the MQTTClient_messageArrived() callback function. This callback function must be specified when you call MQTTClient_setCallbacks(). Dc -> pointer to the MQTTClient_deliveryComplete() callback function. If your application publishes synchronously, or if you don’t want to check for successful delivery, you can set it to NULL.

int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options);
Copy the code

This function attempts to connect the previously created client to the MQTT server using the specified options. Handle -> Pointer to the MQTT client handle. The handle is populated with a client reference successfully returned from the function options-> pointer to a valid MQTTClient_connectOptions structure.

int MQTTAsync_subscribe(MQTTAsync handle, const char* topic, int qos, MQTTAsync_responseOptions* response)
Copy the code

This feature attempts to subscribe customers to a single topic, which may contain wildcards. This function also specifies the quality of service. Handle -> Pointer to the MQTT client handle. The handle is populated with the topic-> subscribed to by the client reference successfully returned from the function, which can be wildcard. Qos -> Subscription request quality of service

int MQTTAsync_sendMessage(MQTTAsync handle, const char* destinationName, const MQTTAsync_message* message,
													 MQTTAsync_responseOptions* response)
Copy the code

This feature attempts to subscribe customers to a single topic, which may contain wildcards. This function also specifies the quality of service. Handle -> Pointer to the MQTT client handle. The handle is populated with a client reference successfully returned from the function topicName-> the subject associated with the information. MSG -> pointer to a valid MQTTClient_message structure containing the payload of the message to be published and the property DT -> pointer to MQTTClient_deliveryToken. When the function returns successfully, dt is assigned the token representing the message. If the pass token is not used in the program, set it to NULL.

Five, the implementation

1. Structure introduction

The C++ layer needs a class to manage the creation, connection, message sending, and topic subscription of our MQTT. The JAVA layer needs a class to interact with the C++ layer and provide the necessary methods from the JAVA layer to call the C++ layer methods and to call the JAVA methods back via JNI when the C++ layer messages arrive. The message is thrown to the JAVA layer as well as the necessary callbacks, such as a failed connection, a failed message delivery, and a failed subscription.

2. Callback Settings in asynchronous mode

The MQTTAsync_setCallbacks method is used to set up callbacks for disconnection, message arrival, message delivery. These can simply be called passive callbacks. Active callbacks, such as at connect, The connection success and failure callbacks can be set using the onSuccess and onFailure fields of MQTTAsync_connectOptions. The MQTTAsync_responseOptions onSuccess and onFailure fields can be set to send success and failure callbacks. Sending successful and failed callbacks can be set via the onSuccess and onFailure fields of MQTTAsync_responseOptions.

3. Introduce C engineering problems for C++

extern "C" {
#include "mqtt/MQTTAsync.h"
};
Copy the code

C++ layer child threads call JAVA class methods

JNIEnv *jniEnv;
    if(javaVM->AttachCurrentThread(&jniEnv, 0) ! = JNI_OK) {return;
    }
    jniEnv->CallVoidMethod();
    javaVM->DetachCurrentThread();
Copy the code

For example, you can create a JavaCall class. The main field holds the Java virtual machine instance and the id of each method, as follows:

class WlJavaCall {

public:
    _JavaVM *javaVM = NULL;
    JNIEnv *jniEnv = NULL;
    jmethodID jmid_on_msg_arrived;
    jmethodID jmid_on_connection_success;
    jmethodID jmid_on_connection_failed;
    jmethodID jmid_on_connection_lost;
    jmethodID jmid_on_send_success;
    jmethodID jmid_on_send_failed;
    jmethodID jmid_on_subscribe_success;
    jmethodID jmid_on_subscribe_failed;
    jobject jobj;

public:
    WlJavaCall(_JavaVM *javaVM, JNIEnv *env, jobject *jobj);

    ~WlJavaCall();

    void onMsgArrived(const char *topic, int size, jbyte *msg);

    void onConnectionSuccess();

    void onConnectionFailed(int code, const char*msg);

    void onConnectionLost(const char *cause);

    void onSendSuccess();

    void onSendFailed(const char *cause);

    void onSubscribeSuccess();

    void onSubscribeFailed();

    void release();
};
Copy the code

In the constructor we get the id of the corresponding method

WlJavaCall::WlJavaCall(_JavaVM *vm, JNIEnv *env, jobject *obj) {
    javaVM = vm;
    jniEnv = env;
    jobj = *obj;
    jobj = env->NewGlobalRef(jobj);
    jclass jlz = jniEnv->GetObjectClass(jobj);
    if(! jlz) {return;
    }
    jmid_on_msg_arrived = jniEnv->GetMethodID(jlz, "onMsgArrived"."(Ljava/lang/String; [B)V");
    jmid_on_connection_success = jniEnv->GetMethodID(jlz, "onConnectionSuccess"."()V");
    jmid_on_connection_failed = jniEnv->GetMethodID(jlz, "onConnectionFailed"."(ILjava/lang/String;) V");
    jmid_on_connection_lost = jniEnv->GetMethodID(jlz, "onConnectionLost"."(Ljava/lang/String;) V");
    jmid_on_send_success = jniEnv->GetMethodID(jlz, "onSendSuccess"."()V");
    jmid_on_send_failed = jniEnv->GetMethodID(jlz, "onSendFailed"."(Ljava/lang/String;) V");
    jmid_on_subscribe_success = jniEnv->GetMethodID(jlz, "onSubscribeSuccess"."()V");
    jmid_on_subscribe_failed = jniEnv->GetMethodID(jlz, "onSubscribeFailde"."()V");
}
Copy the code

This way we can easily call methods in JAVA classes from other C++ classes

5.JAVA class method multithreading problem

Because we use MQTT asynchronous mode at the bottom, so when calling JAVA layer methods, it is inevitable to encounter multithreading problems, so the method is locked when necessary.

6. The message is passed to the UI thread

When the message arrives, we first receive the message in the C++ layer, then call a JAVA class method and throw it into the JAVA layer, so we are still in the child thread, so we can pass the received message to the UI thread through a Handler.

Reference: www.cnblogs.com/edan/p/1040…