最近,项目中要实现基于MQTT协议IM功能。此时,觉得头疼,赶紧问问MQTT的官网,看看这玩意到底是怎么一回事。看过之后,哇哦,还挺神奇的,了解之后,我们自己就可以开发即时通讯了,还用什么第三方的类库啊。好了,闲话不多说,我们进入正题吧。
概览
MQTT是一个轻量的发布订阅模式消息传输协议,专门针对低带宽和不稳定网络环境的物联网应用设计。
MQTT官网: http://mqtt.org
MQTT V3.1.1协议规范: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
1.开放消息协议,简单易实现
2.发布订阅模式,一对多消息发布
3.基于TCP/IP网络连接
4.1字节固定报头,2字节心跳报文,报文结构紧凑
5.消息QoS支持,可靠传输保证
MQTT协议广泛应用于物联网、移动互联网、智能硬件、车联网、电力能源等领域。
1.物联网M2M通信,物联网大数据采集
2.Android消息推送,WEB消息推送
3.移动即时消息,例如Facebook Messenger
4.智能硬件、智能家具、智能电器
5.车联网通信,电动车站桩采集
6.智慧城市、远程医疗、远程教育
7.电力、石油与能源等行业市场
1.在build.gradle文件中添加所需的包。
compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
以为这两个一添加就完了嘛???对,没有,如果只是这样的话,是永远收不到推送的,最关键的就是第二步。
2.在AndroidManifest.xml文件中添加如下代码:
<service android:name="org.eclipse.paho.android.service.MqttService" />
有人可能会有疑问,还要添加这行嘛,对,没错,必须添加。
现在所有的准备工作做完了,让我们连接服务器吧。
我在代码中,书写了相关的注释,大家看代码,这里就不多解释了,如果有不懂的,可以看看上面的中文连接的MQTT协议。
public class MqttTest {
private static final String TAG = "MqttTest";
private static final String MQTT_BROKER = "后台的地址"; // Broker URL or IP Address
private static final String MQTT_PORT = "端口号"; // Broker Port
private static final String MQTT_URL_FORMAT = "tcp://%s:%s"; // URL Format normally don't change
private String myTopic = "test/topic";
private ScheduledExecutorService scheduler;
private MqttAsyncClient mqttClient;
private String userName = "admin"; // 连接的用户名
private String passWord = "123456"; //连接的密码
private String mDeviceId = ""; // Device ID, Secure.ANDROID_ID
private ConnectivityManager mConnectivityManager; // To check for connectivity changes
private MqttConnectOptions options;
private Context context;
public MqttTest(Context context) {
this.context = context;
}
/**
* 初始化相关数据
*/
public void init() {
try {
//host为主机名,test为clientid即连接MQTT的客户端ID,一般以客户端唯一标识符表示,MemoryPersistence设置clientid的保存形式,默认为以内存保存
String url = String.format(Locale.US, MQTT_URL_FORMAT, MQTT_BROKER, MQTT_PORT);
String clientId = String.valueOf(System.currentTimeMillis());
mqttClient = new MqttAsyncClient(url, clientId, new MemoryPersistence());
//MQTT的连接设置
options = new MqttConnectOptions();
//设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接
options.setCleanSession(true);
//设置连接的用户名
options.setUserName(userName);
//设置连接的密码
options.setPassword(passWord.toCharArray());
// 设置超时时间 单位为秒
options.setConnectionTimeout(10);
// 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
options.setKeepAliveInterval(20);
//设置回调
options.setAutomaticReconnect(true);//设置自动重连
// mqttAsyncClient.connect(connOpts).waitForCompletion();
mqttClient.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
//连接丢失后,一般在这里面进行重连
if (isNetworkAvailable()) {
reconnectIfNecessary();
}
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
//subscribe后得到的消息会执行到这里面
Log.e(TAG, "message=:" + message.toString());
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
//publish后会执行到这里
long messageId = token.getMessageId();
Log.e(TAG, "messageId=:" + messageId);
}
});
} catch (Exception e) {
e.printStackTrace();
}
mConnectivityManager = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
context.registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
/**
* Query's the NetworkInfo via ConnectivityManager
* to return the current connected state
* 通过ConnectivityManager查询网络连接状态
*
* @return boolean true if we are connected false otherwise
* 如果网络状态正常则返回true反之flase
*/
private boolean isNetworkAvailable() {
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
return (info == null) ? false : info.isConnected();
}
/**
* Checkes the current connectivity
* and reconnects if it is required.
* 重新连接如果他是必须的
*/
public synchronized void reconnectIfNecessary() {
if (mqttClient == null || !mqttClient.isConnected()) {
connect();
}
}
private void connect() {
new Thread(new Runnable() {
@Override
public void run() {
try {
mqttClient.connect(options);
// 连接成功之后,处理相关逻辑
} catch (Exception e) {
e.printStackTrace();
// 连接出错,可以设置重新连接
}
}
}).start();
}
/**
* 调用init() 方法之后,调用此方法。
*/
public void startReconnect() {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
if (!mqttClient.isConnected() && isNetworkAvailable()) {
connect();
}
}
}, 0 * 1000, 10 * 1000, TimeUnit.MILLISECONDS);
}
/**
* Receiver that listens for connectivity chanes
* via ConnectivityManager
* 网络状态发生变化接收器
*/
private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i("BroadcastReceiver", "Connectivity Changed...");
if (!isNetworkAvailable()) {
Toast.makeText(context, "网络错误", Toast.LENGTH_SHORT).show();
scheduler.shutdownNow();
} else {
startReconnect();
}
}
};
}
嗯,相关连接后台的数据,基本上就这么多,看后天给你返回的数据是怎么样,自己拿上用即可。
最后,加上github的例子,
https://github.com/honeylife/paho.mqtt.javascript,可以下载跑起来,看看相关的例子,逻辑特别严谨。
我会在下面的文章,写即时通讯的相关代码,以及聊天界面的实现,还有图片切换时键盘高度的控制等。