如果您使用的是Eclipse,请查看 此博客文章 。
在给定的代码中,消息将首先被添加到内部队列,发布者将在建立连接时从内部队列发送消息到RabbitMQ。如果连接断开,消息将被添加回队列。
本指南假定您已经下载,安装并设置了适用于Android Studio的所有内容 。
首先创建一个新的Android项目,打开Android Studio,然后进入文件 - >新建 - >新建项目。
1.配置你的新项目
- 输入以下指定的项目信息。
- 选择您的应用程序将运行的形状因子
- 选择是否要将活动添加到您的应用程序。在这个例子中,我们选择 Blank Activity 来为项目获取自动生成的文件。
- 自定义活动
2.将Java AMQP库添加到项目中
RabbitMQ开发了一个优秀的 Java AMQP库。该库的完整API文档可以在这里找到 。
我们需要包含RabbitMQ Java客户端库并将jar文件引用到项目中。在Android Studio中,您可以在与应用程序相同的级别创建一个libs文件夹。复制并放置在这个libs文件夹中。标记所有的jar文件,并按下 “添加为库...” ,如下图所示。
你可以通过打开build.gradle来确认libs已经被添加为库了, 并且在依赖关系下检查 , 所有的文件都应该在那里。
dependencies {
...
compile files('libs/rabbitmq-client.jar')
...
}
注意:只有当您使用Android Gradle插件0.7.0并在稍后运行应用程序时发生错误“APK中复制的文件复制”时,您需要将packagingOptions添加到您的build.gradle文件中,如此 处所述。
android {
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
}
3. Android Manifest,互联网许可
我们需要告诉Android系统,这个应用程序被允许访问互联网。打开位于项目根目录下的AndroidManifest.xml文件。在关闭/清单标签之前添加用户权限android.permission.INTERNET。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cloudamqp.rabbitmq"
android:versionCode="1"
android:versionName="1.0">
......
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>
4.开始编码
布局
为应用程序创建视图。.xml布局文件可以在res-> layout下找到。我们这里有一个根ScrollView包含一个
EditText
a
Button
和a
TextView
EditText将被用作将要发送的文本的输入字段。文本将在按下按钮时发布,订阅者收到的所有消息都将被打印到TextView。
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
...
<EditText
android:id="@+id/text"
android:layout_width="fill_parent"
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" />
...
</ScrollView>
发布
创建一个内部消息队列。在这种情况下是一个 BlockingDeque
使用。 Blockingqueues 实现被设计为主要用于生产者 - 消费者队列。
private BlockingDeque<String> queue = new LinkedBlockingDeque>String>();
void publishMessage(String message) {
try {
Log.d("","[q] " + message);
queue.putLast(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
为ConnectionFactory
连接工厂创建一个设置函数 封装一组连接配置参数,在本例中为CLOUDAMQP_URL。该URL可以在您的实例的控制面板中找到。
ConnectionFactory factory = new ConnectionFactory();
private void setupConnectionFactory() {
String uri = "CLOUDAMQP_URL";
try {
factory.setAutomaticRecoveryEnabled(false);
factory.setUri(uri);
} catch (KeyManagementException | NoSuchAlgorithmException | URISyntaxException e1) {
e1.printStackTrace();
}
创建从内部队列发布消息的发布者。如果发现异常,则将消息添加回队列。如果连接中断,发布者将尝试每5秒重新连接一次。
当我们执行不是即时的操作(比如连接到rabbitMQ的网络访问)时,需要线程(“后台”或“工作者”线程或使用AsyncTask类)。
我们将使用扇出交换。扇出交换将消息路由到与其绑定的所有队列,并且路由密钥被忽略。如果N个队列被绑定到扇出交换机,那么将被发布到该交换机的新消息复制并传送到所有N个队列。扇出交换是消息广播路由的理想选择。
public void publishToAMQP()
{
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("amq.fanout", "chat", null, message.getBytes());
Log.d("", "[s] " + message);
ch.waitForConfirmsOrDie();
} catch (Exception e){
Log.d("","[f] " + message);
queue.putFirst(message);
throw e;
}
}
} catch (InterruptedException e) {
break;
} catch (Exception e) {
Log.d("", "Connection broken: " + e.getClass().getName());
try {
Thread.sleep(5000); //sleep and then try again
} catch (InterruptedException e1) {
break;
}
}
}
}
});
publishThread.start();
}
订户
我们现在已经创建了发布者,现在是创建订阅者的时候了。订户将采取一个处理程序作为参数。消息到达时,处理程序将把消息打印到屏幕上。当连接中断时,订阅线程将尝试每5秒重新连接一次。
void subscribe(final Handler handler)
{
subscribeThread = new Thread(new Runnable() {
@Override
public void run() {
while(true) {
try {
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.basicQos(1);
DeclareOk q = channel.queueDeclare();
channel.queueBind(q.getQueue(), "amq.fanout", "chat");
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(q.getQueue(), true, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
Log.d("","[r] " + message);
Message msg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("msg", message);
msg.setData(bundle);
handler.sendMessage(msg);
}
} catch (InterruptedException e) {
break;
} catch (Exception e1) {
Log.d("", "Connection broken: " + e1.getClass().getName());
try {
Thread.sleep(5000); //sleep and then try again
} catch (InterruptedException e) {
break;
}
}
}
}
});
subscribeThread.start();
}
从函数调用上面列出的所有函数 onCreate
订阅函数使用的处理程序也是在onCreate中创建的。必须使用处理程序,因为只能从主要写入到GUI。
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupConnectionFactory();
publishToAMQP();
setupPubButton();
final Handler incomingMessageHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String message = msg.getData().getString("msg");
TextView tv = (TextView) findViewById(R.id.textView);
Date now = new Date();
SimpleDateFormat ft = new SimpleDateFormat ("hh:mm:ss");
tv.append(ft.format(now) + ' ' + message + '\n');
}
};
subscribe(incomingMessageHandler);
}
void setupPubButton() {
Button button = (Button) findViewById(R.id.publish);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
EditText et = (EditText) findViewById(R.id.text);
publishMessage(et.getText().toString());
et.setText("");
}
});
}
当应用程序被添加下面的代码销毁时,订阅和发布步骤都可以被中断 onDestroy
Thread subscribeThread;
Thread publishThread;
@Override
protected void onDestroy() {
super.onDestroy();
publishThread.interrupt();
subscribeThread.interrupt();
}
完整的代码可以从github,github.com/cloudamqp/android-example中看到并下载
https://www.cloudamqp.com/blog/2015-07-29-rabbitmq-on-android.html 。