如何在Android中基于Go对象通过gomobile实现MQTT客户端监听器
问题核心原因
你的实现存在两个核心问题:
gomobile bind不支持直接将Go自定义函数类型、第三方库的自定义类型(比如mqtt.Client)转换为Java可识别的类型- 你用全局变量存储接收的消息,没有主动通知Java侧更新,自然无法触发持续回调
可行解决方案
1. Go侧定义可跨语言识别的回调接口
gomobile 支持将Go的interface类型自动转换为Java接口,你只需要在Go侧定义回调规范,Java侧实现后传入Go即可,完美绕开类型限制。
package mqttlib import ( "errors" "fmt" mqtt "github.com/eclipse/paho.mqtt.golang" ) // MQTTCallback 回调接口,gomobile会自动生成对应的Java接口 type MQTTCallback interface { OnMessageReceived(topic string, payload string) OnConnected() OnConnectionLost(err string) } // 封装实例,避免全局变量冲突,支持多客户端实例 type MQTTClient struct { client mqtt.Client cb MQTTCallback } // NewMQTTClient 对外暴露的构造方法,Java侧可以直接创建实例 func NewMQTTClient() *MQTTClient { return &MQTTClient{} } // SetCallback 给Java侧提供设置回调的方法 func (m *MQTTClient) SetCallback(cb MQTTCallback) { m.cb = cb }
2. 改造MQTT事件handler,主动触发Java回调
收到MQTT事件时直接调用传入的回调实例方法,不要把消息存在全局变量里:
// 内部消息处理handler func (m *MQTTClient) messagePubHandler(c mqtt.Client, msg mqtt.Message) { if m.cb != nil { m.cb.OnMessageReceived(msg.Topic(), string(msg.Payload())) } } // 内部连接成功handler func (m *MQTTClient) connectHandler(c mqtt.Client) { if m.cb != nil { m.cb.OnConnected() } } // 内部连接断开handler func (m *MQTTClient) connectLostHandler(c mqtt.Client, err error) { if m.cb != nil { m.cb.OnConnectionLost(err.Error()) } }
3. 封装MQTT操作方法,不要暴露内部自定义类型
所有对外暴露的方法参数、返回值都只用基础类型(字符串、数字、布尔、error等),不要把第三方库的自定义类型暴露给Java侧:
// InitConnect 初始化MQTT连接 func (m *MQTTClient) InitConnect(broker string, port int, clientId string, username string, password string) error { opts := mqtt.NewClientOptions() opts.AddBroker(fmt.Sprintf("tcp://%s:%d", broker, port)) opts.SetClientID(clientId) opts.SetUsername(username) opts.SetPassword(password) opts.SetDefaultPublishHandler(m.messagePubHandler) opts.OnConnect = m.connectHandler opts.OnConnectionLost = m.connectLostHandler m.client = mqtt.NewClient(opts) if token := m.client.Connect(); token.Wait() && token.Error() != nil { return token.Error() } return nil } // Subscribe 订阅主题 func (m *MQTTClient) Subscribe(topic string, qos int) error { if m.client == nil || !m.client.IsConnected() { return errors.New("client not connected") } token := m.client.Subscribe(topic, byte(qos), nil) token.Wait() return token.Error() } // Publish 发布消息 func (m *MQTTClient) Publish(topic string, qos int, retained bool, payload string) error { if m.client == nil || !m.client.IsConnected() { return errors.New("client not connected") } token := m.client.Publish(topic, byte(qos), retained, payload) token.Wait() return token.Error() } // Disconnect 断开连接 func (m *MQTTClient) Disconnect(timeout uint) { if m.client != nil && m.client.IsConnected() { m.client.Disconnect(timeout) } }
4. Android Java侧实现回调
编译生成AAR包导入Android项目后,直接实现自动生成的MQTTCallback接口即可,注意回调默认在Go的子线程触发,要更新UI需要切到主线程:
// 实现回调接口 MQTTCallback callback = new MQTTCallback() { @Override public void onMessageReceived(String topic, String payload) { // 需要更新UI的话用runOnUiThread切换到主线程 Log.d("MQTT", "收到消息:主题=" + topic + " 内容=" + payload); } @Override public void onConnected() { Log.d("MQTT", "连接成功"); } @Override public void onConnectionLost(String err) { Log.d("MQTT", "连接断开:" + err); } }; // 调用Go封装的方法 MQTTClient client = Mqttlib.newMQTTClient(); client.setCallback(callback); try { client.initConnect("broker.emqx.io", 1883, "android_test_001", "emqx", "public"); client.subscribe("topic/test", 1); } catch (Exception e) { e.printStackTrace(); }
注意事项
- 所有对外暴露的Go方法,参数和返回值不要使用第三方库的自定义类型,只能用基础类型、你自己定义的
interface或者struct - 如果需要同时创建多个MQTT客户端,直接在Java侧创建多个
MQTTClient实例即可,不会互相冲突 - 回调方法内不要做耗时操作,耗时逻辑放到Java侧单独处理
内容的提问来源于stack exchange,提问作者Axy
相关产品推荐
相关产品推荐

