消息推送,类似于微信来新消息时出现在通知栏那种情景。很多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消息推送平台搭建相关推荐

  1. 京东大规模消息推送平台搭建实践

    背景 每个app或者业务都有将信息推送到用户客户端的需求.作为中台的推送平台,需要为公司内部许多个不同app同时提供可用,稳定的推送服务,因此我们消息推送平台应运而生. 推送平台架构 名词解释: dt ...

  2. 友盟小米收不到推送消息_一个轻量级、可插拔的Android消息推送框架。一键集成推送(极光推送、友盟推送、华为、小米推送等)...

    XPush 一个轻量级.可插拔的Android消息推送框架.一键集成推送(极光推送.友盟推送.华为.小米推送等),提供有效的保活机制,支持推送的拓展,充分解耦推送和业务逻辑,解放你的双手! 在提iss ...

  3. .net 实时通信_基于 RabbitMQ 的实时消息推送

    实现服务器端推送的几种方式 Web 应用都是基于 HTTP 协议的请求/响应模式,无法像 TCP 协议那样保持长连接,因此 Web 应用就很难像手机那样实现实时的消息推送.就目前来看,Web 应用的消 ...

  4. android 消息推送方法,一种基于Android系统的消息推送方法技术方案

    [技术实现步骤摘要] 本专利技术涉及一种基于Android系统的消息推送方法,属于计算机 技术介绍 推送功能在手机应用开发中越来越重要,已经成为手机开发的必选项.消息推送,就是在互联网上通过定期传送用 ...

  5. 基于 RabbitMQ 的实时消息推送

    博主新开公众号"不太灵光的程序员" , 关注公众号,每日八点有干货推送 1 实现服务器端推送的几种方式 Web 应用都是基于 HTTP 协议的请求/响应模式,无法像 TCP 协议那 ...

  6. Android 消息推送

    Android 消息推送 那位大哥做过啊 android是用服务做吧? pushsharp 雲推送 你要有服務器來服務 不是云推送吧 去搜這個,有server,有client,有demo 自己做的话, ...

  7. Android 生态消息推送平台介绍

    一.手机厂商平台 华为消息推送服务 华为推送(Push)是为开发者提供的消息推送平台,建立了从云端到手机端的消息推送通道,使应用可以将最新信息及时通知用户,从而构筑良好的用户关系,提升用户的感知和活跃 ...

  8. Android—消息推送机制

    知识点: 长连接与短连接 端内与端外推送 通知与透传消息 服务保活(心跳机制) 长连接和短连接 消息推送需要客户端与服务端进行连接,因此分为两种方式 长连接,在TCP层握手成功后,不立即断开连接,并在 ...

  9. Android消息推送 解决方案

    前言 鉴于现在运营需求的增强,消息推送在Android开发中应用的场景是十分常见 如电商的活动宣传.资讯类产品进行新闻推送等等 推送消息截图 今天,我将全面介绍Android中实现消息推送的7种主流解 ...

最新文章

  1. CAS、原子操作类的应用与浅析及Java8对其的优化
  2. 转录组背景、环境设置(目录管理)
  3. asp.net core监控—引入Prometheus(五)
  4. 5个常见的Hibernate异常及其解决方法
  5. Qt仿win7自动顶部最大化左侧右侧半屏效果
  6. shell脚本实现一个彩色进度条
  7. php算法和数据结构
  8. C语言基础之--sizeof()运算符的使用以及注意
  9. WPF界面设计中常用的一些代码片段及属性
  10. 地址坐标LA3708:Graveyard
  11. java中重载构造方法的例程_Java构造函数使用多种方法重载
  12. YYText识别链接和点击事件
  13. 关于使用Curvy插件实现物体沿指定路径移动的方法
  14. TOAD 调试存储过程的方法
  15. MTK-EIS电子防抖-gyro校准
  16. 银河帝国----基地
  17. C语言的逻辑右移和算术右移
  18. 深度学习GPU卡的理解(一)
  19. USB_HID协议基础
  20. android jsoup 课程表,使用jsoup爬取数据实现android课程表

热门文章

  1. 给大家推荐几款小型的Linux发行版
  2. A lpad、rpad填充函数--mysql随即订单生成
  3. MongoDB的skip,limit,sort执行顺序,以及aggregate
  4. 尴尬!买了几台华为路由器却不会配置~看完就会了
  5. Windows系统设置定时任务自动执行Jmeter脚本
  6. 【DevOps】什么是混沌工程?
  7. CAD - 工具管理篇 - 安装
  8. 编译与代码安全之认识(二):Source2Source源码混淆方法
  9. 联通TEWA 800改桥接
  10. 静态网页制作代码php,php生成静态页面的简单示例