背景
监控地图的时候,需要在地图上实时动态的显示小车的一些状态。这只是一部分很小的功能,但是这就需要实时的获取小车数据了,根据获取的小车数据来实时动态更新地图上小车的跑动显示。那么这里采用从RabbitMQ上获取实时包消息,然后根据此实时包消息来做相对应的处理。
什么是RabbitMQ
MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBM WEBSPHERE MQ等等。
几个重要概念
Exchange:交换机,决定了消息路由规则;
Queue:消息队列;
Channel:进行消息读写的通道;
Bind:绑定了Queue和Exchange,意即为符合什么样路由规则的消息,将会放置入哪一个消息队列;
rabbitmq百度百科
我上面写了一些需要了解rabbitmq的必要知识点。不过想要加深理解的话可以点击rabbitmq百度百科来查看详细的rabbitmq相关的内容。
Android端的两种应用场景
如下图所示:
由图示可以清楚明白的了解Android端与RabbitMQ的使用场景。
场景一:手机端需要发布一条消息到RabbitMQ上,这时候你公司的服务端人员F(这里用F表示人名)就可以与RabbitMQ交互进行相应的处理。比如F会监听他创建的队列,是否接收到Android端发布过来的消息。如果确定Android端发布了一条消息到F创建的队列中,那么F会发布另外的消息到RabbitMQ上,这时候我就需要创建一个队列来获取消息,然后根据获取的消息拿到数据进行相关操作,这样就完成了一个场景的应用。
场景二:这个场景会比场景一稍微简单,你不用发布消息到RabbitMQ上了,但是你需要创建随机的队列来接收RabbitMQ的消息。为什么是随机的,因为可以确保你的应用在多台设备上都能接收到RabbitMQ的消息。根据获取的消息你可以拿到相应的数据然后进行相关操作,这样就完成了这个场景的应用。
场景疑惑解答
这里我对场景一和场景二中一些可能会对大家产生疑惑的问题进行解答。
你可能会思考我的Android端发布的消息怎么确定会发送到F创建的队列中呢?
因为你发送消息的时候,需要指定交换机的名称和路由键名称;而F创建的队列需要通过路由键和交换机进行绑定,那么你Android端发布的消息就会发送到F创建的队列中(你的交换机名称和路由键肯定要和F创建的队列相关联的交换机名称和路由键相同。)
你可能会思考F发布了一个消息到RabbitMQ上,Android端接收消息是不是和上面的原理一样呢?
告诉你,是一样的。我简述一下原理,首先F发布消息的时候,也是需要指定交换机的名称和路由键的。所以只要Android端此时创建的队列是通过相同的路由键绑定到相同名称的交换机上时,就可以接收F发布到RabbitMQ上的消息。
你可能会思考场景二中,为什么随机创建的队列可以在多台设备上运行呢?如果只创建一个队列,多个客户端可不可以共享这个队列呢?
首先,你创建的一个队列A对应设备A,队列B对应设备B。然后交换机会发送消息到所有通过路由键与之绑定的队列中,那么队列A和队列B就都可以接收到消息了,故你的应用在多台设备上都可以接收到消息了。第二个问题通过查阅相关的文档找到了解答,说是可以实现多个客户端共享一个已知名称的队列,如下:
我的选择
这里我获取消息的时候是选择随机创建多个队列且连接断开后队列会自动删除的方案。对于创建一个队列,多个客户端共享的例子需要考虑的东西很多,只要哪里没考虑好就容易出问题。而且说实话我原来想实现的,但是发现了好多问题,而且还不知道具体什么原因导致的,就没去深究其原因,项目紧啊。所以如果有哪位大神能成功实现的话,欢迎留言,鄙人一定好好学习一二。
Android Studio中添加依赖
compile 'com.rabbitmq:amqp-client:4.4.1'
在gradle(module的)中添加上面一句,然后等studio同步,同步完成后就可以使用RabbitMQ的相关api了。有很多的版本大家可以去选择添加,这里我选择了4.4.1版本,点击查看所有版本。
发布消息代码
private ConnectionFactory factory = new ConnectionFactory();// 声明ConnectionFactory对象
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
setUpConnectionFactory();// 连接设置
publishToAMPQ(Constants.MQ_ROUTINGKEY_MAP_REQUEST);// 发送消息到MQ
publishMessage("messageData");
}
});
/**
* 连接设置
*/
private void setUpConnectionFactory() {
factory.setHost(Constants.MQ_HOST);//主机地址:192.168.1.105
factory.setPort(Constants.MQ_PORT);// 端口号:5672
factory.setUsername(Constants.MQ_USERNAME);// 用户名
factory.setPassword(Constants.MQ_PASSWORD);// 密码
factory.setAutomaticRecoveryEnabled(true);// 设置连接恢复
}
// 创建内部消息队列,供消费者发布消息用
private BlockingDeque<String> queue = new LinkedBlockingDeque<>();
private void publishToAMPQ(final String routingKey) {
publishThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
// 创建连接
Connection connection = factory.newConnection();
// 创建通道
Channel ch = connection.createChannel();
ch.confirmSelect();
while (true) {
String message = queue.takeFirst();
try {
// 发布消息
ch.basicPublish(Constants.MQ_EXCHANGE_MAP, routingKey, null, message.getBytes());
ch.waitForConfirmsOrDie();
} catch (Exception e) {
queue.putFirst(message);
throw e;
}
}
} catch (InterruptedException e) {
break;
} catch (Exception e) {
Log.d("TAG_Publish", "Connection broken: " + e.getClass().getName());
try {
Thread.sleep(5000); //sleep and then try again
} catch (InterruptedException e1) {
break;
}
}
}
}
});
publishThread.start();
void publishMessage(String message) {
//向内部阻塞队列添加一条消息
try {
queue.putLast(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- 上面的代码就是发布消息的代码。整体逻辑:点击发布消息按钮,发布String类型数据,值为messageData的消息到RabbitMQ上。想看我获取消息的代码请看Android端发布消息到RabbitMQ、从RabbitMQ获取消息(二)。
=======================================================================