文章标题
来源:互联网 发布:sql数据库培训多少钱 编辑:程序博客网 时间:2024/06/05 03:08
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
效果如下:
- 文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题 文章标题 文章标题 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- RecyclerView和ListView的异同
- 关于SSL错误, SSLError: [Errno 1] _ssl.c:510: error:14090086:SSL routines
- 每天五分钟linux(5)-rm
- xUtils 多线程断点下载文件
- java定时器问题
- 文章标题
- C#设计模式(四)抽象工厂模式
- Connectify在开发中的使用
- MVC,MVP,MVVM的区别
- 《剑指offer》数组中重复的数字
- 学生信息管理系统--知识小结(一)
- tensorflow1.1/构建双向神经网络识别mnist
- ANR
- mysql update from 结果集