目录
前言
Android 系统作为移动设备领域的主流操作系统之一,其功能的丰富性和复杂性不断提升。通知作为 Android 系统与用户交互的重要方式,对于用户体验和应用的有效运行起着至关重要的作用。
通知能够在不打断用户当前操作的情况下,向用户传递关键信息,如消息提醒、系统状态更新等。短信,qq、微信消息通知,所有你下滑屏幕看到的消息都是通知。
正文
使用
通知有一个系统模版,模板定义了大致样式,我们可以调用系统的api来设置模板里模块的内容。
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channel_id")
.setContentTitle("您有新消息")
.setContentText("来自好友的问候")
.setSmallIcon(R.drawable.message_icon)
.setPriority(NotificationCompat.PRIORITY_HIGH);
Notification notification = builder.build();
notificationManager.notify(1, notification);
如果要构建一个自定义布局的通知,我们需要借助于RemoteViews(创建自定义通知布局或桌面小部件布局的类),我们可以设置一个小布局,一个大布局的xml,最后也是调用api来设置视图。
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
RemoteViews notificationLayout = new RemoteViews(getPackageName(), R.layout.notification_small);
RemoteViews notificationLayoutExpanded = new RemoteViews(getPackageName(), R.layout.notification_large);
Notification customNotification = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setStyle(new NotificationCompat.DecoratedCustomViewStyle())
.setCustomContentView(notificationLayout)
.setCustomBigContentView(notificationLayoutExpanded)
.build();
notificationManager.notify(1, customNotification);
另外我们还可以通过add Action API来添加操作等,都是调用API。
轮询
所以,我们知道显示通知就调用系统API就行了,但是我们什么时候显示通知,显示通知的数据内容其实都要由服务端来决定,那我们怎么保证服务端要推送消息,这边都可以迅速知道呢。
- 应用可以在后台通过 Service 或者 JobScheduler 来执行定期任务,轮询服务检查是否有新的消息或事件需要通知用户。
- 后台任务定期向服务器发送请求,检查是否有新的数据或事件需要通知用户。例如,检查是否有新的消息、更新、警报等。
- 服务器返回新的通知数据,应用处理这些数据,并根据业务逻辑向用户发送通知。
- 应用使用 NotificationManager 来创建和发布通知。通过调用 NotificationManager.notify() 方法,通知会被系统接收并调度,根据通知的优先级、通知通道的设置、当前设备状态(如电量模式、勿扰模式)等因素最终展示在通知栏或锁屏界面上。
But,Android 系统对后台任务和轮询的频率有一定的限制,尤其是在设备进入 Doze 模式时;轮询的频率不好决定,太快增加服务端负载,太慢消息不及时。
长连接
像大型应用能够实现即时弹出通知,肯定不是依赖频繁的轮询。频繁的轮询不仅会消耗大量的电量和网络资源,也会受到系统的限制,无法保证及时性。更好的使用方式使用的是长连接或推送服务。
长连接是一种保持客户端与服务器之间的连接长时间活跃的技术。通过 TCP 协议,应用可以在后台维持一个长连接,服务器可以随时向客户端推送消息。
当服务器有新消息时,立即通过这条长连接将消息发送给客户端,客户端接收到消息后立刻调用系统服务展示通知。
维持长连接通常需要发送心跳包,以保持连接的活跃状态。心跳包的频率是经过优化的,以平衡电量消耗与连接的实时性;应用最佳实践是基于设备的网络状态、系统状态动态调整心跳包的发送频率,确保在低电量模式下也能保持基本的消息推送。
推送服务 --apple推送原理
推送服务例如FCM(Google 提供的跨平台消息传递服务),是开发者可选的推送方式;推送服务器是google的,由系统全局和推送服务器建立联系,而开发者只需要和谷歌的推送服务器建立连接,这也是apple通知的原理,这使得手机更省电,但是也像苹果手机老被吐槽的,消息通知来的太慢。
许多厂商也在定制 ROM 中集成了自家的推送服务,以提高消息的及时性和稳定性,如华为push,小米push。
在国内,由于 Google 服务的限制,像腾讯、阿里等公司会使用自建的推送服务系统,比如腾讯的信鸽推送(XGPush)或阿里的阿里云推送。
但是自建推送服务终逃不过长连接,来保持实时获取数据,有多少台设备就有多少个长连接,这不是巨大的压力吗?
以下这几种设计可以减轻压力
- 端口复用:服务器端使用端口复用技术把多个连接共享同一个端口,通过一个端口管理大量的并发连接,而不为每个连接分配一个独立的端口。
- 负载均衡:负载均衡器将来自不同设备的连接分发到多个服务器上。这些服务器可以共享同一个公共端口,而实际的连接处理分布在多个服务器节点上。
- 非阻塞 I/O:服务器使用非阻塞 I/O 操作(如 select, poll, epoll),使一个线程能够管理多个连接。这样,服务器不需要为每个连接都创建一个线程或进程,从而大幅度减少资源消耗。
- 连接池化:对于短连接务器可以使用连接池来复用连接资源,从而避免频繁地创建和销毁连接所带来的开销。
- 水平扩展:增加服务器节点的方式来水平扩展系统的容量,分担大量连接的压力。负载均衡器将连接分配给不同的服务器节点。
- QUIC:基于 UDP 的 QUIC 协议在传输层实现多路复用和连接迁移,进一步减少对端口的依赖,同时提高传输效率和连接的稳定性。