基于SpringBoot、RabbitMQ的Android消息推送平台搭建
消息推送,类似于微信来新消息时出现在通知栏那种情景。很多APP都有这个功能。现在有很多第三方平台可以实现这个需要,但是有的公司对所要推送的消息保密要求比较高,不希望被第三方看到,可以使用此种方式进行消息推送。
下面使用SpringBoot、RabbitMQ搭建一个消息推送平台,实现java、C#或者python等后台把消息推送到Android客户端的功能。文末有源码。RabbitMQ的安装网上有很多教程,这里不再讲述。
一、java服务端的代码如下:
这个java程序只是为了向外提供接口,然后java、C#或者python等后台调用此接口,把所要推送的消息传递给此java程序,此程序再传递给RabbitMQ服务器。具体业务逻辑不在这个程序中。
pom.xml中引入如下依赖:
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.6.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>
application.yml文件如下:
server:port: 10086 #设置微服务端口spring:application:name: app_msg_push #设置微服务名
启动类如下:
@SpringBootApplication
public class MsgPushApplication
{public static void main(String[] args){SpringApplication.run(MsgPushApplication.class, args);}
}
获取RabbitMQ链接的类:
/*** 配置RabbitMQ连接*/
public class ConnectionUtils
{public static Connection getConnection() throws IOException, TimeoutException{ConnectionFactory conn = new ConnectionFactory();conn.setHost("127.0.0.1"); //RabbitMQ服务所在的ip地址conn.setPort(15672); //RabbitMQ服务所在的端口号conn.setVirtualHost("/test");conn.setUsername("test");conn.setPassword("test");return conn.newConnection();}
}
controller如下:
@Controller
public class MsgPushController
{//设置交换机名称private final static String EXCHANGE_NAME = "app_msg_push_exchange";/*** 把消息传递给RabbitMQ服务器** @param msg :消息* @param to :routingKey匹配规格* @return* @throws Exception*/@PostMapping("send_msg")public ResponseEntity<String> sendMessage(@RequestParam(value = "msg", required = true) String msg, @RequestParam(value = "to", required = true) String to) throws Exception{if (StringUtils.isEmpty(msg) || StringUtils.isEmpty(to)){return ResponseEntity.ok("请设置请求参数!");}// 获取到RabbitMQ服务器连接Connection connection = ConnectionUtils.getConnection();// 获取通道Channel channel = connection.createChannel();// 声明exchange,指定类型为topic,true表示进行持久化channel.exchangeDeclare(EXCHANGE_NAME, "topic", true);// 发送消息,并且指定routing key为tochannel.basicPublish(EXCHANGE_NAME, to, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());//如果发送消息到RabbitMQ服务器失败,每隔10秒再次重试channel.confirmSelect();if (!channel.waitForConfirms()){Timer timer = new Timer();TimerTask timerTask = new TimerTask(){@Overridepublic void run(){try{channel.basicPublish(EXCHANGE_NAME, to, null, msg.getBytes());} catch (IOException e){e.printStackTrace();}}};timer.schedule(timerTask, 0, 10000);}channel.close();connection.close();return ResponseEntity.ok("推送成功!");}
}
如上设置好后,调用的链接就是:http://127.0.0.1:10086/send_msg,参数就是msg=“你好”,另一个参数为to=“app.msg.test”,请求方式为POST,可以使用Postman或者RestClient进行测试。
二、Android客户端的代码如下:
在模块中的build.gradle中引入如下依赖:
implementation 'com.rabbitmq:amqp-client:4.4.1'
链接RabbitMQ的工具类如下:
/*** 配置RabbitMQ连接*/
public class ConnectionUtils
{public static Connection getConnection() throws IOException, TimeoutException{ConnectionFactory conn = new ConnectionFactory();conn.setHost("127.0.0.1"); //RabbitMQ服务所在的ip地址conn.setPassword("5672"); //RabbitMQ服务所在的端口号conn.setVirtualHost("/test");conn.setUsername("test");conn.setPassword("test");return conn.newConnection();}
}
SP工具类如下:
/*** SharedPreferences的一个工具类。 调用setParam就能保存String, Integer, Boolean, Float,* Long类型的参数。同样调用getParam就能获取到保存在手机里面的数据* */
public class SPUtils {/*** 保存在手机里面的文件名*/private static final String FILE_NAME = "config";/*** 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法* * @param context* 上下文* * @param key* 字段名* * @param object* 字段值*/public static void setParam(Context context, String key, Object object) {// 删除String type = object.getClass().getSimpleName();SharedPreferences sp = context.getSharedPreferences(FILE_NAME,Context.MODE_PRIVATE);SharedPreferences.Editor editor = sp.edit();if ("String".equals(type)) {editor.putString(key, (String) object);} else if ("Integer".equals(type)) {editor.putInt(key, (Integer) object);} else if ("Boolean".equals(type)) {editor.putBoolean(key, (Boolean) object);} else if ("Float".equals(type)) {editor.putFloat(key, (Float) object);} else if ("Long".equals(type)) {editor.putLong(key, (Long) object);}editor.commit();}/*** 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值* * @param context* 上下文* * @param key* 字段名* * @param defaultObject* 字段默认值* * @return*/public static Object getParam(Context context, String key,Object defaultObject) {String type = defaultObject.getClass().getSimpleName();SharedPreferences sp = context.getSharedPreferences(FILE_NAME,Context.MODE_PRIVATE);if ("String".equals(type)) {return sp.getString(key, (String) defaultObject);} else if ("Integer".equals(type)) {return sp.getInt(key, (Integer) defaultObject);} else if ("Boolean".equals(type)) {return sp.getBoolean(key, (Boolean) defaultObject);} else if ("Float".equals(type)) {return sp.getFloat(key, (Float) defaultObject);} else if ("Long".equals(type)) {return sp.getLong(key, (Long) defaultObject);}return null;}
}
MainActivity如下:
public class MainActivity extends AppCompatActivity
{@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); //页面自己定义,消息推送无需任何控件Intent intent = new Intent(this, NotificationService.class);startService(intent); //启动监听消息推送的服务}
}
监听消息推送的Service如下:
/*** 后台监听消息推送的服务*/
public class NotificationService extends Service
{private Context context;private static String EXCHANGE_NAME = "app_msg_push_exchange";private static String QUEUE_NAME = "";@Overridepublic IBinder onBind(Intent intent){return null;}@Overridepublic void onCreate(){ // 当服务第一次被开启的时候调用super.onCreate();context = this;//设置每个用户对应一个队列if (TextUtils.isEmpty((String) SPUtils.getParam(context, "QUEUE_NAME", ""))){SPUtils.setParam(context, "QUEUE_NAME", "app_msg_push_queue_" + UUID.randomUUID());}QUEUE_NAME = (String) SPUtils.getParam(context, "QUEUE_NAME", "");getDataFromMQ();}/*** 从消息队列获取数据*/private void getDataFromMQ(){new Thread(new Runnable(){@Overridepublic void run(){try{// 获取到连接Connection connection = ConnectionUtils.getConnection();// 获取通道final Channel channel = connection.createChannel();// 声明队列channel.queueDeclare(QUEUE_NAME, true, false, false, null);// 绑定队列到交换机,同时指定需要订阅的routing key。channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "app.msg.#");// 定义队列的消费者DefaultConsumer consumer = new DefaultConsumer(channel){@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{// body 即消息体final String msg = new String(body);showNotifictionIcon(context, getAppName(context), msg);//手动设置ACKchannel.basicAck(envelope.getDeliveryTag(), false);}};// 监听队列,手动ACKchannel.basicConsume(QUEUE_NAME, false, consumer);} catch (IOException e){e.printStackTrace();} catch (TimeoutException e){e.printStackTrace();}}}).start();}public void showNotifictionIcon(Context context, String title, String content){NotificationManager manager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);NotificationCompat.Builder builder = new NotificationCompat.Builder(context);builder.setAutoCancel(true);//点击后消失builder.setLargeIcon((getBitmap(context)));//设置通知栏消息标题的头像builder.setSmallIcon(R.mipmap.ic_launcher);builder.setDefaults(NotificationCompat.DEFAULT_SOUND);//设置通知铃声builder.setContentTitle(title);//设置标题builder.setContentText(content);//设置内容builder.setPriority(Notification.PRIORITY_DEFAULT); //设置该通知优先级//利用PendingIntent来包装我们的intent对象,使其延迟跳转 设置通知栏点击意图builder.setContentIntent(createIntent(context, title + content));manager.notify(new Random().nextInt(20), builder.build());}/*** 创建通知栏消息点击后跳转的intent。*/public PendingIntent createIntent(Context context, String data){Intent intent = new Intent(context, NotifyClickReceiver.class);Bundle mBundle = new Bundle();mBundle.putString("data", data);intent.putExtras(mBundle);intent.setAction("com.example.myapp");PendingIntent contentIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);return contentIntent;}/*** 获取应用图标bitmap*/public static Bitmap getBitmap(Context context){PackageManager packageManager = null;ApplicationInfo applicationInfo = null;try{packageManager = context.getApplicationContext().getPackageManager();applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);} catch (PackageManager.NameNotFoundException e){applicationInfo = null;}Drawable d = packageManager.getApplicationIcon(applicationInfo); //xxx根据自己的情况获取drawableBitmapDrawable bd = (BitmapDrawable) d;Bitmap bm = bd.getBitmap();return bm;}/*** 获取应用程序名称*/public static String getAppName(Context context){try{PackageManager packageManager = context.getPackageManager();PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);int labelRes = packageInfo.applicationInfo.labelRes;return context.getResources().getString(labelRes);} catch (Exception e){e.printStackTrace();}return null;}
}
点击通知栏后需要跳转,使用一个广播监听并获取数据:
/*** 点击通知栏跳转的广播*/
public class NotifyClickReceiver extends BroadcastReceiver
{@Overridepublic void onReceive(Context context, Intent intent){String action = intent.getAction();String data = intent.getStringExtra("data");if (action.equals("com.example.myapp")){Intent i = new Intent(context, getMsgActivity.class);i.putExtra("data", data);i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //从四大组件的其他三个组件跳转到Activity,需要设置FLAG_ACTIVITY_NEW_TASKcontext.startActivity(i);}}
}
获取消息并处理的Activity如下:
public class getMsgActivity extends Activity
{private TextView tv;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_getmsg);tv = findViewById(R.id.tv);Intent intent = getIntent();String data = intent.getStringExtra("data");tv.setText("获取到新消息:" + data);}
}
别忘了在AndroidManifest.xml中添加权限,并且声名Service和BroadcastReceiver:
......<uses-permission android:name="android.permission.INTERNET" />......<receiver android:name=".NotifyClickReceiver" />
<service android:name=".NotificationService" />
.....
先在几个Android设备上运行上面APP;再通过POST请求访问http://127.0.0.1:10086/send_msg,并设置参数为msg=“你好”,另一个参数为to=“app.msg.test”;运行可见APP都能够接收到消息,亲测可用。后面就可以在java、C#或者python等后台调用此接口,实现消息推送功能。
上面的RoutingKey是写死的app.msg.#,推送的时候是app.msg.test;在实际使用中,读者可以自定义设置RabbitMQ的RoutingKey,以匹配合适的用户进行推送消息。
如果所要推送的人数(即消息队列个数)比较多的话,并发量比较大,可以搭建RabbitMQ集群解决此问题。
基于SpringBoot、RabbitMQ的Android消息推送平台搭建相关推荐
- 京东大规模消息推送平台搭建实践
背景 每个app或者业务都有将信息推送到用户客户端的需求.作为中台的推送平台,需要为公司内部许多个不同app同时提供可用,稳定的推送服务,因此我们消息推送平台应运而生. 推送平台架构 名词解释: dt ...
- 友盟小米收不到推送消息_一个轻量级、可插拔的Android消息推送框架。一键集成推送(极光推送、友盟推送、华为、小米推送等)...
XPush 一个轻量级.可插拔的Android消息推送框架.一键集成推送(极光推送.友盟推送.华为.小米推送等),提供有效的保活机制,支持推送的拓展,充分解耦推送和业务逻辑,解放你的双手! 在提iss ...
- .net 实时通信_基于 RabbitMQ 的实时消息推送
实现服务器端推送的几种方式 Web 应用都是基于 HTTP 协议的请求/响应模式,无法像 TCP 协议那样保持长连接,因此 Web 应用就很难像手机那样实现实时的消息推送.就目前来看,Web 应用的消 ...
- android 消息推送方法,一种基于Android系统的消息推送方法技术方案
[技术实现步骤摘要] 本专利技术涉及一种基于Android系统的消息推送方法,属于计算机 技术介绍 推送功能在手机应用开发中越来越重要,已经成为手机开发的必选项.消息推送,就是在互联网上通过定期传送用 ...
- 基于 RabbitMQ 的实时消息推送
博主新开公众号"不太灵光的程序员" , 关注公众号,每日八点有干货推送 1 实现服务器端推送的几种方式 Web 应用都是基于 HTTP 协议的请求/响应模式,无法像 TCP 协议那 ...
- Android 消息推送
Android 消息推送 那位大哥做过啊 android是用服务做吧? pushsharp 雲推送 你要有服務器來服務 不是云推送吧 去搜這個,有server,有client,有demo 自己做的话, ...
- Android 生态消息推送平台介绍
一.手机厂商平台 华为消息推送服务 华为推送(Push)是为开发者提供的消息推送平台,建立了从云端到手机端的消息推送通道,使应用可以将最新信息及时通知用户,从而构筑良好的用户关系,提升用户的感知和活跃 ...
- Android—消息推送机制
知识点: 长连接与短连接 端内与端外推送 通知与透传消息 服务保活(心跳机制) 长连接和短连接 消息推送需要客户端与服务端进行连接,因此分为两种方式 长连接,在TCP层握手成功后,不立即断开连接,并在 ...
- Android消息推送 解决方案
前言 鉴于现在运营需求的增强,消息推送在Android开发中应用的场景是十分常见 如电商的活动宣传.资讯类产品进行新闻推送等等 推送消息截图 今天,我将全面介绍Android中实现消息推送的7种主流解 ...
最新文章
- CAS、原子操作类的应用与浅析及Java8对其的优化
- 转录组背景、环境设置(目录管理)
- asp.net core监控—引入Prometheus(五)
- 5个常见的Hibernate异常及其解决方法
- Qt仿win7自动顶部最大化左侧右侧半屏效果
- shell脚本实现一个彩色进度条
- php算法和数据结构
- C语言基础之--sizeof()运算符的使用以及注意
- WPF界面设计中常用的一些代码片段及属性
- 地址坐标LA3708:Graveyard
- java中重载构造方法的例程_Java构造函数使用多种方法重载
- YYText识别链接和点击事件
- 关于使用Curvy插件实现物体沿指定路径移动的方法
- TOAD 调试存储过程的方法
- MTK-EIS电子防抖-gyro校准
- 银河帝国----基地
- C语言的逻辑右移和算术右移
- 深度学习GPU卡的理解(一)
- USB_HID协议基础
- android jsoup 课程表,使用jsoup爬取数据实现android课程表