RabbitMQ topic主题的例子包含线程
==
我参考网上的资料做了两个接收者和一个生产者,总共有两个项目。接收者里指定要接收的信息:如1.#和2.#,生产者随意发送信息,只有1和2开头的路由器关键字会被接收其他的不行。
发往主题类型的转发器的消息不能随意的设置选择键(routing_key),必须是由点隔开的一系列的标识符组成。标识符可以是任何东西,但是一般都与消息的某些特性相关。绑定键和选择键的形式一样。主题类型的转发器背后的逻辑和直接类型的转发器很类似:一个附带特殊的选择键将会被转发到绑定键与之匹配的队列中。
RabbitMQ中消息传递模型的核心思想是:生产者不直接发送消息到队列。实际的运行环境中,生产者是不知道消息会发送到那个队列上,她只会将消息发送到一个交换器,交换器也像一个生产线,她一边接收生产者发来的消息,另外一边则根据交换规则,将消息放到队列中。交换器必须知道她所接收的消息是什么?它应该被放到那个队列中?它应该被添加到多个队列吗?还是应该丢弃?这些规则都是按照交换器的规则来确定的。
参考了以下两个例子:
http://blog.csdn.net/lmj623565791/article/details/37706355
https://www.cloudamqp.com/blog/2015-07-29-rabbitmq-on-android.html
https://github.com/cloudamqp/android-example
首先先看生产者的模块:
我们创建一个生产者线程。生产者仅能将消息发送到一个交易所。一个交易所是一个非常简单的事物。 在它的一遍,它从生产者那里接收消息,另一边将消息推送到队列中。 这个交换所必须清楚的知道它所接收到的消息要如何处理。是否将它附加到一个特别的队列中? 是否将它附加到多个队列中?或者是否它应该被丢弃。规则的定义是由交换类型决定的。
我们创建一个服务器的连接:这里我们连接到本地机器上的代理,因此它是localhost。如果我们想连接到不同机器上的代理,只需要说明它的主机名和IP地址。
ConnectionFactory factory = new ConnectionFactory();
private void setupConnectionFactory(){
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("test");
factory.setPassword("test123");
}
接着我们获取到连接connection 以及mq通道channel
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
RabbitMQ消息模型的核心理念是生产者永远不会直接发送任何消息给队列,一般的情况生产者甚至不知道消息应该发送到哪些队列。
相反的,生产者只能发送消息给转发器(Exchange)。转发器是非常简单的,一边接收从生产者发来的消息,另一边把消息推送到队列中。转发器必须清楚的知道消息如何处理它收到的每一条消息。是否应该追加到一个指定的队列?是否应该追加到多个队列?或者是否应该丢弃?这些规则通过转发器的类型进行定义。
接着创建一个这种类型的交易所exchange.我们必须声明一个发送队列,然后我们把消息发送到这个队列上这里是topic(主题)用来匹配的转发器类型
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
既然有了转发器类型接下来就要输入信息还有转发器关键字了,因为消息内容message 是二进制数组,所以你可以随你喜好编码,队列通过路由关键字routing_key绑定。其中takeFirst检索并移除此队列的第一个元素,如果有必要,直到一个元素可用等。
String message = queue.takeFirst();
String routing_key = queue2.takeFirst();
然后开始发送信息,我们用下面的语句。其中第一个参数是交易所的名字。如果是空字符串说明它是默认的或者匿名的交易所:第二个路由关键字存在的话,消息通过路由关键字的名字路由到特定的队列上。如:(发送者1.abc接收者就要1.#)或者(两者一样发送者q接收者就q)。
channel.basicPublish(EXCHANGE_NAME,routing_key,null,message.getBytes());
这就是生产的部分,以下完整的生产者代码
package com.example.dong24;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//服务器的连接:
setupConnectionFactory();
setupPubButton();
//用于从线程中获取数据,更新ui
final Handler incomingMessageHandler = new Handler(){
@Override
public void handleMessage(Message msg){
String message = msg.getData().getString("msg");
String message2 = msg.getData().getString("msg2");
TextView tv = (TextView)findViewById(R.id.textView);
TextView tv2 = (TextView)findViewById(R.id.textView2);
Date now = new Date();
SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
tv.append(ft.format(now) + ' ' + message + "\n");
tv2.append(ft.format(now) + ' ' +message2 + "\n");
Log.i("test","msg " + message);
}
};
//开启发送消息的生产者线程
publishToAMQP(incomingMessageHandler);
}
void setupPubButton(){
Button button = (Button)findViewById(R.id.publish);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EditText et = (EditText)findViewById(R.id.text);
publishMessage(et.getText().toString());
et.setText("");
}
});
Button button2 = (Button)findViewById(R.id.publish2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EditText et = (EditText)findViewById(R.id.text2);
publishMessage2(et.getText().toString());
et.setText("");
}
});
}
Thread publishThread;
@Override
protected void onDestroy(){
super.onDestroy();
publishThread.interrupt();
}
private BlockingDeque<String> queue = new LinkedBlockingDeque<String>();
void publishMessage(String message){
try{
Log.d("","[q] " + message);
//putLast在这个队列的末尾插入指定元素,如果空间成为提供必要的等待。
queue.putLast(message);
}catch (InterruptedException e){
e.printStackTrace();
}
}
private BlockingDeque<String> queue2 = new LinkedBlockingDeque<String>();
void publishMessage2(String message) {
try {
Log.d("", "[q ] " + queue2);
queue2.putLast(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 连接设置,我们创建一个服务器的连接:
*/
//抽象的socket连接,注意协议版本的处理以及授权,诸如此类的事情。
//这里我们连接到本地机器上的代理,因此它是localhost。如果我们想连接到不同机器上的代理,只需要说明它的主机名和IP地址。
ConnectionFactory factory = new ConnectionFactory();
private void setupConnectionFactory(){
factory.setHost("192.168.X.XXX");//可以用自己的IP地址
factory.setPort(5672);
factory.setUsername("test");
factory.setPassword("test123");
}
/**
* 生产者线程
*/
// 生产者仅能将消息发送到一个交易所。一个交易所是一个非常简单的事物。
// 在它的一遍,它从生产者那里接收消息,另一边将消息推送到队列中。
// 这个交换所必须清楚的知道它所接收到的消息要如何处理。是否将它附加到一个特别的队列中?
// 是否将它附加到多个队列中?或者是否它应该被丢弃。规则的定义是由交换类型决定的。
void publishToAMQP(final Handler handler){
publishThread = new Thread(new Runnable() {
private static final String EXCHANGE_NAME = "topic_logs";
@Override
public void run() {
while(true){
try{
// 获取到连接以及mq通道
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.confirmSelect();
//创建一个这种类型的交易所.我们必须声明一个发送队列,然后我们把消息发送到这个队列上
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
while (true){
//输入关键字
// 声明一个队列是幂等的,仅仅在要声明的队列不存在时才创建。消息内容是二进制数组,所以你可以随你喜好编码。
//takeFirst检索并移除此队列的第一个元素,如果有必要,直到一个元素可用等。
String message = queue.takeFirst();
String routing_key = queue2.takeFirst();
try{
//这第一个参数是交易所的名字。空字符串说明它是默认的或者匿名的交易所:路由关键字存在的话,消息通过路由关键字的名字路由到特定的队列上。
channel.basicPublish(EXCHANGE_NAME,routing_key,null,message.getBytes());
System.out.println(" [x] Sent routingKey = " + routing_key + " ,msg = " + message + ".");
Log.d("","[s] "+message);
channel.waitForConfirmsOrDie();
//从message池中获取msg对象更高效
//对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者 Handler.obtainMessage()获取。
// Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的, 才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。
//并不需要担心消息池中的消息过多,它是有上限的,上限为10个。 Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是 调用的Message.obtain()。
Message msg = handler.obtainMessage();
Message msg2 = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("msg", message);
bundle.putString("msg2", routing_key);
msg.setData(bundle);
msg2.setData(bundle);
handler.sendMessage(msg);
}catch (Exception e){
Log.d("","[f] " + message);
//putFirst在这个队列的前面插入指定元素,如果空间成为提供必要的等待。
queue.putFirst(message);
queue2.putFirst(routing_key);
throw e;
}
}
}catch (InterruptedException e){
break;
}catch (Exception e){
Log.d("","Connection broken:" + e.getClass().getName());
try{
Thread.sleep(5000);
}catch (InterruptedException e1){
break;
}
}
}
}
});
publishThread.start();
}
}
然后再看消费者的模块:
跟创建发送者相同,我们打开一个连接和一个通道,声明一个我们要消息的队列。注意要与发送的队列相匹配。
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("test");
factory.setPassword("test123");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
接着声明转发器类型,我们把消息发送到这个队列上这里是topic(主题)用来匹配的转发器类型
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
我们都为队列指定了一个特定的名称。能够为队列命名对我们来说是很关键的,我们需要指定消费者为某个队列。当我们希望在生产者和消费者间共享队列时,为队列命名是很重要的。
不过,对于我们的日志系统我们并不关心队列的名称。我们想要接收到所有的消息,而且我们也只对当前正在传递的数据的感兴趣。为了满足我们的需求,需要做两件事:第一, 无论什么时间连接到Rabbit我们都需要一个新的空的队列。为了实现,我们可以使用随机数创建队列,或者更好的,让服务器给我们提供一个随机的名称。第二, 一旦消费者与Rabbit断开,消费者所接收的那个队列应该被自动删除。Java中我们可以使用queueDeclare()方法,不传递任何参数,来创建一个非持久的、唯一的、自动删除的队列且队列名称由服务器随机产生。
我们再创建队列,队列名中包含一个随机队列名。使用无参数调queueDeclare()方法,我们创建一个自动产生的名字,不持久化,独占的,自动删除的队列。例如名字像amq.gen-JzTY20BRgKO-HjmUJj0wLg。
String queueName = channel.queueDeclare().getQueue();
然后输入信息还有转发器关键字了,队列通过路由关键字routing_key绑定。要与发送者的关键字保持一致。如:(发送者1.abc接收者就要1.#)或者(两者一样发送者q接收者就q)。其中takeFirst检索并移除此队列的第一个元素,如果有必要,直到一个元素可用等。
String routing_key = queue3.takeFirst();
之后是绑定,我们需要告诉交易所发送消息给我们的队列上。这交易所和队列之间的关系称之为一个绑定。接收所有routingKey相关的消息,将队列绑定到消息交换机exchange上。一个绑定是一个交换所和一个队列之间的关系。这个很容易理解为:这个队列是对这交易所的消息感兴趣。
绑定可以附带一个额外的参数routingKey。为了与避免basicPublish方法(发布消息的方法)的参数混淆,我们准备把它称作绑定键(binding key)。
channel.queueBind(queueName,EXCHANGE_NAME,routing_key);
注意我们在这里同样声明了一个队列。以为我们可能在发送者之前启动接收者,在我们从中获取消息之前我们想要确定这队列是否真实存在。 我们通知服务器通过此队列给我们发送消息。因此服务器会异步的给我们推送消息,在这里我们提供一个回调对象用来缓存消息, 直到我们准备好再使用它们。这就是QueueingConsumer所做的事。
QueueingConsumer consumer = new QueueingConsumer(channel);
监听队列,自动返回完成 false手动
channel.basicConsume(queueName,true,consumer);
最后获取消息,如果没有消息,这一步将会一直阻塞,开启nextDelivery阻塞方法(内部实现其实是阻塞队列的take方法)
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
routing_key = delivery.getEnvelope().getRoutingKey();
这就是消费的部分,以下完整的消费者代码
package com.example.dong25;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//服务器的连接:
setupConnectionFactory();
setupPubButton();
//用于从线程中获取数据,更新ui
final Handler incomingMessageHandler = new Handler(){
@Override
public void handleMessage(Message msg){
String message3 = msg.getData().getString("msg3");
String message4 = msg.getData().getString("msg4");
TextView tv3 =(TextView)findViewById(R.id.textView3);
TextView tv4 =(TextView)findViewById(R.id.textView4);
Date now = new Date();
SimpleDateFormat ft = new SimpleDateFormat("hh.mm.ss");
tv3.append(ft.format(now) + ' ' + message3 + "\n");
tv4.append(ft.format(now) + ' ' + message4 + "\n");
Log.i("test","msg3 " + message3);
}
};
//开启消费者线程 接收消息的消费者. 接收者将会输出从RabbitMQ中获取到来自发送者的消息。接收者会一直保持运行,等待消息
subscribe(incomingMessageHandler);
subscribe2(incomingMessageHandler);
}
void setupPubButton(){
Button button3 = (Button)findViewById(R.id.publish3);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EditText et = (EditText)findViewById(R.id.text3);
publishMessage3(et.getText().toString());
et.setText("");
}
});
Button button4 = (Button)findViewById(R.id.publish4);
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EditText et = (EditText)findViewById(R.id.text4);
publishMessage4(et.getText().toString());
et.setText("");
}
});
}
Thread subscribeThread;
Thread subscribeThread2;
@Override
protected void onDestroy(){
super.onDestroy();
subscribeThread.interrupt();
subscribeThread2.interrupt();
}
private BlockingDeque<String> queue3 = new LinkedBlockingDeque<String>();
void publishMessage3(String message){
try{
Log.d("","[q] " + message);
//putLast在这个队列的末尾插入指定元素,如果空间成为提供必要的等待。
queue3.putLast(message);
}catch (InterruptedException e){
e.printStackTrace();
}
}
private BlockingDeque<String> queue4 = new LinkedBlockingDeque<String>();
void publishMessage4(String message){
try{
Log.d("","[q] " + message);
queue4.putLast(message);
}catch (InterruptedException e){
e.printStackTrace();
}
}
/**
* 连接设置,我们创建一个服务器的连接:
*/
//抽象的socket连接,注意协议版本的处理以及授权,诸如此类的事情。
//这里我们连接到本地机器上的代理,因此它是localhost。如果我们想连接到不同机器上的代理,只需要说明它的主机名和IP地址。
ConnectionFactory factory = new ConnectionFactory();
private void setupConnectionFactory(){
factory.setHost("192.168.X.XXX");//可以用自己的IP地址
factory.setPort(5672);
factory.setUsername("test");
factory.setPassword("test123");
}
/**
* 消费者线程
*/
//跟创建发送者相同,我们打开一个连接和一个通道,声明一个我们要消费的队列。注意要与发送的队列相匹配。
void subscribe(final Handler handler){
subscribeThread = new Thread(new Runnable() {
private static final String EXCHANGE_NAME = "topic_logs";
@Override
public void run() {
while (true){
try{
//创建一个连接 创建一个频道
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//同一时刻服务器只会发一条消息给消费者 ,一次只发送一个,处理完成一个再获取下一个
channel.basicQos(1);
// 声明转发器
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
// 队列名中包含一个随机队列名。使用无参数调用queueDeclare()方法,我们创建一个自动产生的名字,不持久化,独占的,自动删除的队列。
// 队列名中包含一个随机队列名。例如名字像amq.gen-JzTY20BRgKO-HjmUJj0wLg。
String queueName = channel.queueDeclare().getQueue();
//输入关键字
// 声明一个队列是幂等的,仅仅在要声明的队列不存在时才创建。消息内容是二进制数组,所以你可以随你喜好编码。
//takeFirst检索并移除此队列的第一个元素,如果有必要,直到一个元素可用等。
String routing_key = queue3.takeFirst();
//我们需要告诉交易所发送消息给我们的队列上。这交易所和队列之间的关系称之为一个绑定。
//接收所有routingKey相关的消息,将队列绑定到消息交换机exchange上
//一个绑定是一个交换所和一个队列之间的关系。这个很容易理解为:这个队列是对这交易所的消息感兴趣。
channel.queueBind(queueName,EXCHANGE_NAME,routing_key);
System.out.println(" [#] Waiting for messages about routingKey. To exit press CTRL+C");
//注意我们在这里同样声明了一个队列。以为我们可能在发送者之前启动接收者,在我们从中获取消息之前我们想要确定这队列是否真实存在。
// 我们通知服务器通过此队列给我们发送消息。因此服务器会异步的给我们推送消息,在这里我们提供一个回调对象用来缓存消息,
// 直到我们准备好再使用它们。这就是QueueingConsumer所做的事。
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,自动返回完成 false手动
channel.basicConsume(queueName,true,consumer);
//循环获取消息
while(true){
//获取消息,如果没有消息,这一步将会一直阻塞,开启nextDelivery阻塞方法(内部实现其实是阻塞队列的take方法)
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
routing_key = delivery.getEnvelope().getRoutingKey();
System.out.println(" [x] Received routingKey = " + routing_key + ",msg = " + message + ".");
Log.d("","[r] "+message);
//从message池中获取msg对象更高效
//对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者 Handler.obtainMessage()获取。
// Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的, 才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。
//并不需要担心消息池中的消息过多,它是有上限的,上限为10个。 Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是 调用的Message.obtain()。
Message msg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("msg3",message);
msg.setData(bundle);
handler.sendMessage(msg);
//putFirst在这个队列的前面插入指定元素,如果空间成为提供必要的等待。
queue3.putFirst(routing_key);
}
}catch (InterruptedException e){
break;
}catch (Exception el){
Log.d("","Connection broken: "+ el.getClass().getName());
try{
Thread.sleep(4000);
}catch (InterruptedException e){
break;
}
}
}
}
});
subscribeThread.start();
}
/**
* 消费者线程2 与1原理一样
*/
void subscribe2(final Handler handler){
subscribeThread2 = new Thread(new Runnable() {
private static final String EXCHANGE_NAME = "topic_logs";
@Override
public void run() {
while (true){
try{
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.basicQos(1);
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
String queueName = channel.queueDeclare().getQueue();
String routing_key2 = queue4.takeFirst();
channel.queueBind(queueName,EXCHANGE_NAME,routing_key2);
System.out.println(" [#] Waiting for messages about routingKey. To exit press CTRL+C");
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName,true,consumer);
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
routing_key2 = delivery.getEnvelope().getRoutingKey();
System.out.println(" [x] Received routingKey = " + routing_key2 + ",msg = " + message + ".");
Log.d("","[r] "+message);
Message msg2 = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("msg4",message);
msg2.setData(bundle);
handler.sendMessage(msg2);
queue4.putFirst(routing_key2);
}
}catch (InterruptedException e){
break;
}catch (Exception el){
Log.d("","Connection broken: "+ el.getClass().getName());
try{
Thread.sleep(4000);
}catch (InterruptedException e){
break;
}
}
}
}
});
subscribeThread2.start();
}
}
在页面方面的代码如下
这是生产者的页面
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" >
<TableLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1">
<EditText
android:id="@+id/text2"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:background="#ffffff"
android:hint="Enter a key"
android:layout_weight="1"/>
<Button
android:id="@+id/publish2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/text"
android:text="Publish routing_key"
android:layout_weight="1"/>
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/publish"
android:textColor="#000000"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>
<EditText
android:id="@+id/text"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:background="#ffffff"
android:hint="Enter a message" />
<Button
android:id="@+id/publish"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/text"
android:text="Publish message" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/publish"
android:textColor="#000000" />
</TableLayout>
</ScrollView>
这是消费者的页面
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" >
<TableLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1">
<EditText
android:id="@+id/text3"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:background="#ffffff"
android:hint="Receive a key"
android:layout_weight="1"/>
<Button
android:id="@+id/publish3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/text"
android:text="Receive routing_key"
android:layout_weight="1"/>
<TextView
android:id="@+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/publish3"
android:textColor="#000000"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1">
<EditText
android:id="@+id/text4"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:background="#ffffff"
android:hint="Receive a key"
android:layout_weight="1"/>
<Button
android:id="@+id/publish4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/text"
android:text="Receive routing_key"
android:layout_weight="1"/>
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/publish3"
android:textColor="#000000"
android:layout_weight="1"/>
</LinearLayout>
</TableLayout>
</ScrollView>
代码的部分就结束了,注意是两个项目所以要两个MainActivity,当然你自己想要合并成一个也可以。我们还要
在AndroidManifest中加入
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="21" />
在build.gradle中加入—-内的内容
android {
----------
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
----------
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.1.0'
testCompile 'junit:junit:4.12'
----------
compile 'com.rabbitmq:amqp-client:4.1.1'
compile files('libs/junit.jar')
compile files('libs/rabbitmq-client.jar')
compile files('libs/commons-cli-1.1.jar')
compile files('libs/commons-io-1.2.jar')
compile files('libs/hamcrest-core.jar')
compile files('libs/rabbitmq-client-tests.jar')
----------
}
以下就是源码
https://github.com/CHENRONG92/Dong23
效果如下: