Flutter IM方案探索——MQTT

7,051 阅读5分钟

Demo 效果图

Android 效果图 iOS 效果图

方案选型——MQTT

  在开始这片文章之前,首先来介绍一下我们今天的两位主角🤪:
  主角一号(IM):即时通讯(Instant Messaging)
  主角二号(MQTT):MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。
  其实在实现项目内的IM功能时有多种选择,为什么选择MQTT呢?🤔首先是考虑到我们项目的多端一致性,我们的项目有多个终端,在其中一个端中已经采纳了MQTT方案成功接入并实现了功能,后续可能打通多端的对话,所以我在自己的项目内也打算采用MQTT消息推送的方案。还有一个重要的原因:MQTT有库!有库!

  看见了吗?闪闪发光的一个库啊,高人气+高关注+高likes,已经充分说明了他有多香。有些小杠精可能会说了:你这用三方库没有技术含量啊,三方库的定制性不高啊,巴拉巴拉~。但是👴这不是在看源码进行定制化处理吗,巨人有肩膀你不踩,非得去脚下,也许有一天你成了巨人但我已经成了巨人肩膀上的巨人,奥利给!🚀准备好在巨人的肩膀上起飞了吗?

MQTT Client 接入

Step1:MQTTAppConnectionState

  首先需要明确的是MQTT的连接状态,这部分通俗易懂,在MQTT可能的状态有连接,未连接,正在连接。用枚举的方式将他们都列举出来。

    enum MQTTAppConnectionState { connected, disconnected, connecting }

非常好!已经完成了第一步,思路逐渐明确👀

Step2:MQTTAppState

  有了MQTT的连接状态之后,还需的是什么呢?我们知道连接状态的目的是什么?当然是在连接成功的时候收发消息,连接断开的时候停止收发消息,并且在收到消息时我们要知道我们要知道,我们收到了什么消息。那我们需要的就是这样一个类,我们叫他MQTTAppState。

    class MQTTAppState with ChangeNotifier{
  MQTTAppConnectionState _appConnectionState = MQTTAppConnectionState.disconnected;
  String _receivedText = '';
  String _historyText = '';

  void setReceivedText(String text) {
    _receivedText = text;
    _historyText = _historyText + '\n' + _receivedText;
    notifyListeners();
  }
  void setAppConnectionState(MQTTAppConnectionState state) {
    _appConnectionState = state;
    notifyListeners();
  }

  String get getReceivedText => _receivedText;
  String get getHistoryText => _historyText;
  MQTTAppConnectionState get getAppConnectionState => _appConnectionState;
}

在这个类中各位可以看到我们定义了我们当前收到的消息receivedText和我们收到的历史消息_historyText,我们定义这两个消息,我们的历史消息其实是一个叠加、换行的过程,每收到一条新消息,我们在原有的基础上加入这个新消息形成一个新的历史消息。同时我们也定义了方法去管理MQTTAppConnectionState.重点!🚩我们的这个类已经继承了ChangeNotifier,熟悉Flutter的同学也都能猜到,这是为了我们方便之后的全局状态管理,没错,这里用的也是Provider技术方案。

Step3:MQTTManager

  需要接入一个功能,一个类库,首先你要有一个全局管理的思想,否则在Flutter这样的框架中你会让自己的UI和业务逻辑很难区分开来。这里的MQTTManager目的就是统一管理你的IM消息收发和消息订阅,作为一个全局manager类,首先我们需要的是定义构造函数,构造函数里一般会放置一个类的必要内容,对于一个MQTT来说,一个必备的类是什么呢?这里需要大家对MQTT有基础的了解,这里我先把答案写给大家。

 MQTTManager({
    @required String host,
    @required String topic,
    @required String identifier,
    @required MQTTAppState state
  }
      ): _identifier = identifier, _host = host, _topic = topic, _currentState = state ;

对于一个MQTT最基础的配置包括host(host地址)topic(订阅主题)identifier(用户身份)state(连接状态)其实这些大家仔细想一下不难理解,这是我们要做到收发消息,区分收件人、发件人的一些基础配置。
  我们还需要一个MQTT服务器!🔧,同样我们需要加入服务器的基础配置

  void initializeMQTTClient(){
    _client = MqttServerClient(_host,_identifier);
    _client.port = 1883;
    _client.keepAlivePeriod = 20;
    _client.onDisconnected = onDisconnected;
    _client.secure = false;
    _client.logging(on: true);

    _client.onConnected = onConnected;
    _client.onSubscribed = onSubscribed;

    final MqttConnectMessage connMess = MqttConnectMessage()
        .withClientIdentifier(_identifier)
        .withWillTopic('willtopic')  set a will message
        .withWillMessage('My Will message')
        .startClean()
        .withWillQos(MqttQos.atLeastOnce);
    _client.connectionMessage = connMess;
  }

  这样一个简单的服务器我们已经给他配置好了基础配置,例如端口号,连接保持时长等,所有信息配置好之后,再给manager加上一些基础的管理方法就可以了。

MQTT的使用

  在我们以上不懈努力下,接下来的一切都显得顺理成章,信手捏来🚅,结合上我们的页面知识。我们可以通过Manager实现我们的MQTT连接

  _connectMQTT()
  {
    manager = MQTTManager(
        host: "test.mosquitto.org",
        topic: "flutter/amp/cool",
        identifier: _userName,
        state: currentAppState);
    manager.initializeMQTTClient();
    manager.connect();
  }

  同时,由于我们已经集成了ChangeNaotifier,我们可以动态的监听我们所订阅的消息🦀,轻轻松松,两行代码。

//当前收到的消息
appState.getReceivedText
//所有历史消息
appState.getHistoryText

发送消息也很简单,一行代码,搞定~😏

mqttManager.publish(message);

整个MQTT框架搭建完成,配上一个炫酷的登录页面,配上一个类似微信的聊天界面,效果正是非常可以了。

项目地址

你把你的star🌟给我交了!哈哈哈哈哈
MQTT Demo

尾声

其实这只是一个简单的MQTT实现方案,大家在体验时可能会遇到消息收发延迟的问题,虽然不是很明显但是也会影响用户体验,这是什么原因呢?🤨因为我们遗忘了我们的好朋友——后端小伙伴,实际开发中,我们要和后端小伙伴合作我们的IM策略,往往是通过后端接口实现IM消息的发送,并且封装我们的监听部分。具体的方案我会在后续开发过程中持续更新,欢迎关注哦~🤩