首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >第一天接手 iOS APNs 推送开发,我是如何从懵逼到通关的?

第一天接手 iOS APNs 推送开发,我是如何从懵逼到通关的?

原创
作者头像
用户11708420
修改2026-05-20 10:21:45
修改2026-05-20 10:21:45
1340
举报

📌 序言:前阵子遇到新需求:“给‘xx事件’接上 iOS 的 APNs 远程推送通知。”接到任务的那一刻,我心里是一片空白的,我此前并没有接触过 ios的生态,也不知道这个APNs是什么东西,面对这个全然陌生的技术领域,我决定从产品入手,先明白项目为什么要进行APNs开发,他是干什么用的?


一、为什么要进行APNs开发?

首先从用户视角和业务视角来看,APNs本质上就是一个“手机消息通知”的功能。就是你每天早上打开手机看到的一个一个消息弹窗,APNs的全称就叫做Apple Push Notification service。

从技术上来看不做 APNs,iOS App 就没有远程推送能力

  • iOS 严格限制 App 后台存活时间,普通 App 退到后台很快被挂起,无法自建长连接保活。苹果只允许通过 APNs(Apple Push Notification service) 做远程推送;所有第三方推送(如 FCM)在 iOS 底层都依赖 APNs。

在搞清楚这个之后,我就知道我要做的其实就是一个消息推送功能,这个依赖苹果的Apple Push Notification service


二、消息推送功能是如何工作的呢?手机上的消息弹窗是谁弄出来的呢?弹窗的具体内容是谁决定的呢?

  • 宏观视角来看(不考虑操作系统底层的细节)手机上的消息弹窗是 iOS 操作系统弄出来的,而不是我们的 App。
  • 前面我们已经知道了“ iOS 严格限制 App 后台存活时间,普通 App 退到后台很快被挂起,无法自建长连接保活。当通知砸向手机时,我们的 App 可能正处于“死亡状态”(被用户彻底杀了进程)。真正一直在后台睁着眼睛、保持清醒的,是 iPhone 的 iOS 操作系统。
  • iOS 系统在手机后台维护了一条跟苹果 APNs 服务器的唯一专属通道。当苹果服务器把消息扔给手机时,iOS 系统负责接住,然后由系统亮起屏幕、发出声音、并在屏幕顶部硬生生砸出一个横幅弹窗

1.既然是系统负责弹窗,那我们后端、苹果服务器、用户手机之间又是怎么配合的呢?

简单来说就是xx事件触发后端 后端把推送消息发给苹果 APNs 服务器,由苹果统一下发到用户 iPhone,弹出消息提醒。

第一阶段:登记收件地址(App 刚安装时)

  1. 用户打开 App,点击“同意接收通知”。
  2. iOS 系统向苹果 APNs 服务器申请了一个该设备专属的“收件地址”,技术上叫 Device Token
  3. App 拿到这个 Device Token 后,立刻调用我们后端的接口,把 Token 传给后端。
  4. 我们的后端把这个 Device Token 存在数据库里,和该用户的 User_ID 绑定。

至此,准备工作完成,后端手里有了给该用户寄信的“门牌号”。

第二阶段:业务触发与投递(xx事件发生时)
  1. 我们后端的xx服务触发了(比如:xx报告已出)。
  2. 后端去数据库里查出该用户的 Device Token,然后把“报告已出”的文字,打包成一个标准的 HTTP/2 POST 请求,发送给苹果 APNs 服务器
  3. 苹果 APNs 服务器收到请求,校验了我们的证书,确认我们是正版后端后,对我们说了一句 200 OK。(到这一步,后端的任务已经彻底结束了)
  4. 苹果 APNs 服务器通过它和 iPhone 之间的长连接,把消息推给用户的手机
  5. 用户的 iOS 系统收到消息,在锁屏界面弹出通知横幅。

2.弹窗的具体内容是谁决定的?

完全由 后端业务 决定!苹果和系统只负责提供“信封”。

苹果极其死板,它为了保证所有 App 的通知在 iPhone 上看起来整齐划一,提供了一个固定格式的 JSON 结构(相当于官方信封)。但信封里面写什么字,100% 由我们后端说了算

比如,只要我们在代码里组装出下面这块数据发送给苹果:

代码语言:javascript
复制
{
  "aps": {
    "alert": {
      "title": "订单发货提醒📦",
      "body": "您购买的潮流球鞋已安排发货,快递单号:SF10086,请注意查收~"
    },
    "sound": "default",
    "badge": 1
  }
}

用户的手机屏幕上就会一字不差地显示这个标题和内容。苹果和 iOS 系统就像顺丰快递员,他们不关心信件里写了什么(是情书还是账单),他们只负责安全、准时地把它送到目的地并展示出来


三、开发的落地

在知道什么是APNs,知道他是如何工作的时候我就要开始开发的逻辑,那我首先就要知道 什么时候发送通知,也就是在什么事件条件下通知苹果 让他发送通知给用户,以及如何通知苹果。

1.什么时候发送通知?

这个问题可以拆成业务上什么时候用户应该接收到通知,技术上何时通知苹果两层

技术服务于业务。第一步和产品经理(PM)在白纸上明确,用户在什么场景下必须收到弹窗

2. 技术上什么时候触发?(后端的代码埋点)

明确了业务场景后,我们就要回到后端的代码世界。我们需要找到对应业务逻辑的终点线,在那里埋下“触发推送”的钩子(Hook)。

简单来说,当数据库里的核心状态发生改变,或者某个关键接口成功返回时,就是技术上通知苹果(APNs)的时刻。在具体的架构实现上,我们通常有以下两种最经典的触发方式:

方式 A:直接调用(同步/异步方法)

—— 适合:系统简单、对推送实时性要求极高的场景。

当业务逻辑执行完毕后,主线程直接在代码里调用推送方法(或者开一个线程异步调用),向苹果的 APNs 服务器发送请求。

方式 B:消息队列(MQ)异步解耦

—— 适合:高并发、架构复杂、对稳定性要求高的中大型系统。

这是目前业界最主流、也最优雅的处理方式。核心业务代码只负责干一件事:把事情做好,然后“发个广播”。

消息队列虽然好,但是太重量级了!

不想引入复杂的 RabbitMQ / Kafka,但又觉得直接用异步线程(方式 A)不够安全、怕丢数据时 还有一个选择, 就是把 DB(数据库)当成一个“轻量级的消息队列”

本质就是在数据库创建一个表,当作队列用,后端定期扫描,处理in process状态的task。它能够发挥消息队列解耦削峰的优势,也不需要部署和维护额外的消息队列集群,减少了运维成本。在小项目中或一些特定的业务场景下,它的缺点,比如 时效性较差 分布式锁问题 数据库性能压力 可以忽略不记

触发方式

实时性

数据可靠性

系统复杂度

适用场景

A. 直接调用 (同步/异步)

极高 (立刻发送)

低 (内存丢了或宕机就没了)

极低 (几行代码搞定)

个人项目、MVP 快速上线、对丢失不敏感的通知。

B. 消息队列 (MQ解耦)

高 (毫秒级延迟)

高 (MQ 支持持久化)

高 (需要运维 MQ 中间件)

用户量大、高并发、核心业务。

C. 数据库轮询 (DB暂存)

较低 (受限于扫描间隔)

极高 (强事务保证)

中等 (需处理重试)

并发不高、但绝对不能漏发的通知

3. 具体怎么发送消息通知呢? ——APNs 的具体实现

(1)APNs 通信的底层原理

在最底层的网络视角看,APNs 的本质是一个基于长连接的高性能双向通信网关

苹果为了让全世界所有的 iOS 设备都能高效接收通知,规定了两个死理:

  1. 强制使用 HTTP/2 协议:相比老旧的 HTTP/1.1,HTTP/2 支持多路复用(Multiplexing)。这意味着你的后端服务器只需要跟苹果保持极少数的几个 TCP 长连接,就能在这几个连接里同时、并发地给成千上万个用户发送不同的 POST 请求,而不需要频繁握手,极大地节省了网络开销。
  2. 强制安全双向验证 (TLS 1.2+):苹果不接受任何明文传输。无论是证书方式还是 Token 方式,你和苹果服务器之间必须建立起坚不可摧的加密信道。

(2)寄信前的准备:你要向 iOS 同学索要的“四大原材料”

按照官方文档,要发起一次成功的投递,后端必须在本地配置好以下四样东西。

代码语言:txt
复制
🔑 鉴权凭证(.p12证书 或 .p8令牌) ──> 证明“我是正版后端”
🏷️ apns-topic(App 的 Bundle ID) ──> 证明“我要发给哪款App”
📍 Device Token(64位十六进制字符串) ──> 证明“我要发给哪台具体的手机”
  1. 鉴权凭证(二选一)
    • .p12 证书:内含数字证书,直接绑定在底层的 TLS 握手阶段(双向 TLS 验证)。
    • .p8 令牌:一个纯文本密钥,后端在发送请求时,需要动态用算法将其算成一串 JWT(JSON Web Token),塞进 HTTP 请求头里。
  2. apns-topic(Bundle ID): 比如 com.jianding.app。因为一个开发者账号下可能有多款 App,你必须明确告诉苹果你想把消息塞进哪一个。
  3. Device Token: 目标 iPhone 真机在注册通知时向苹果申请的唯一软硬件标识(类似 00fc13ad...)。它就是你要投递的精准门牌号

(3)官方规定的标准时序流程

当你的后端要触发一个“鉴定事件通知”时,三方之间的接力赛是这样运行的:

后端发起(Create a POST Request):你的业务满足触发条件(如报告生成),后端捞出该用户的 Device Token,组装好一个标准的 HTTP/2 POST 请求。

网关校验(APNs Validate):请求送达苹果的 APNs 服务器。苹果立刻拆开信封:

  • 检查 TLS 证书或 JWT 令牌是否合法。
  • 检查 apns-topic 是否属于你这个账号。

响应(APNs Response):校验通过,苹果立刻给你的后端返回一个 200 OK

  • 关键认知:到这一步,你后端的任务就已经打完收工了。你不需要等用户的手机亮屏,因为接下来的接力棒由苹果接走。

系统投递(Device Delivery):苹果 APNs 网关在后台通过它跟那台 iPhone 之间长年维持的系统级高开销通道,把消息拍向用户的手机。

系统弹窗(iOS Render):用户的 iOS 操作系统(注意:不是你们的 App 进程)收到数据,硬生生在锁屏或屏幕顶部砸出一个横幅弹窗。


(四)、 核心:拆解标准的底层 HTTP/2 报文规范

为了让你等会儿写代码时知道每一行 Headers 是在干什么,对照官方文档,看看发送时的 HTTP 报文长什么样:

代码语言:javascript
复制
// 1. 伪首部(HTTP/2 Pseudo-Header Fields)
:method = POST
:scheme = https
:path = /3/device/00fc13adff785122b4ad28809...  // 路径里直接拼上 Device Token

// 2. 苹果要求的核心头部(Headers)
host = api.sandbox.push.apple.com              // 开发测试网关(线上用 api.push.apple.com)
apns-topic = com.jianding.app                  // 必填:你的 App 包名
apns-push-type = alert                        // 必填:告诉苹果这是会引起亮屏、声音的用户弹窗
apns-expiration = 0                           // 选填:0 代表手机离线就不缓存,立刻丢弃
apns-priority = 10                            // 选填:10 为最高优先级,代表立刻投递,不合并成批次

// 3. 消息体(DATA Frame / JSON Payload)
{
  "aps" : {
    "alert" : {
      "title" : "商品已发货 🚨",
      "body" : "你购买的xx球鞋已发货"
    },
    "sound" : "default",
    "badge" : 1
  }
}
  • aps 外壳:里面的键值对(alert, sound, badge)是苹果死死定义好的,iOS 系统只认这几个词来渲染弹窗。如果你传 "badge": 1,用户手机屏幕上的 App 图标右上角就会出现数字 1

注意苹果的 APNs 非常死板,它不会帮你做数学加法。 也就是说,如果用户本来就有 2 条未读消息(红点显示 2),此时你后端又触发了一次发货通知,如果你依然在 JSON 里传 "badge": 1,用户的红点不仅不会变成 3,反而会变成 1。在真实的商业项目里,后端数据库通常要有一张表去记录每个用户当前的“未读消息总数”。每次准备发 APNs 推送前,先去数据库里把未读数 $+1$,然后把计算好的最终结果(比如 3)塞进 JSON 里的 "badge": 3 发给苹果。

  • 自定义私货:如果你想在用户点击弹窗时,让 App 自动跳转到对应的鉴定报告页面,你可以在 aps同级外层塞入你自己的键值对,例如 "report_id": 9527

(五)、代码实现——p.8验证与p.12验证的对比


🛠️ 方案一:使用第三方库(Pushy)+ .p8 令牌方式

这是目前工业界最主流、最推荐的搭配。.p8 文件永远不会过期,且一个文件可以给公司旗下所有 App 发通知。我们使用苹果生态里公认最好用的 pushy 库来搞定。

1. 引入依赖 (Maven)

XML

代码语言:xml
复制
<dependency>
    <groupId>com.eatthepath</groupId>
    <groupId>pushy</groupId>
    <version>0.15.4</version>
</dependency>

2. Java 实现代码

Java

代码语言:java
复制
import com.eatthepath.pushy.apns.ApnsClient;
import com.eatthepath.pushy.apns.ApnsClientBuilder;
import com.eatthepath.pushy.apns.PushNotificationResponse;
import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification;
import com.eatthepath.pushy.apns.util.TokenUtil;

import java.io.File;
import java.security.interfaces.ECPrivateKey;
import java.util.concurrent.CompletableFuture;

public class ApnsWithPushyP8 {
    public static void main(String[] args) {
        // 1. 准备向 iOS 同学索要的【.p8 原材料】
        String teamId = "ABC123XYZ7";                 // 苹果开发者团队 ID
        String keyId = "KEY9527ABC";                  // .p8 密钥文件自己的 ID
        String p8FilePath = "/path/to/AuthKey_KEY9527ABC.p8"; // .p8 文件的绝对路径
        
        String bundleId = "com.jianding.app";         // App 的唯一包名 (apns-topic)
        String deviceToken = "00fc13adff785122b4ad...";   // 目标测试手机的 Token

        ApnsClient apnsClient = null;
        try {
            // 2. 库在底层会自动读取 .p8 并用算法计算出 JWT 加密签名
            ECPrivateKey privateKey = TokenUtil.loadPrivateKeyFromP8File(new File(p8FilePath));
            
            apnsClient = new ApnsClientBuilder()
                    .setSigningKey(privateKey, teamId, keyId) // 注入密钥三件套
                    .setApnsServer(ApnsClientBuilder.DEVELOPMENT_SERVER) // 指定沙盒测试环境
                    .build();

            // 3. 组装标准 JSON 内容
            String payload = "{\"aps\":{\"alert\":{\"title\":\"发货通知\",\"body\":\"使用Pushy + .p8 发射成功!\"},\"sound\":\"default\"}}";

            // 4. 打包发送对象
            SimpleApnsPushNotification notification = new SimpleApnsPushNotification(deviceToken, bundleId, payload);

            // 5. 异步发送并等待结果
            CompletableFuture<PushNotificationResponse<SimpleApnsPushNotification>> future = apnsClient.sendNotification(notification);
            PushNotificationResponse<SimpleApnsPushNotification> response = future.get();

            if (response.isAccepted()) {
                System.out.println("【成功】通过 .p8 成功送达苹果网关!");
            } else {
                System.err.println("【失败】原因: " + response.getRejectionReason());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (apnsClient != null) { apnsClient.close(); }
        }
    }
}

🛠️ 方案二:使用 Java 11 原生代码 + .p12 证书方式

这是无需任何外部依赖(零 Maven 配置)的最小实现。我们将 .p12 证书直接绑定到 Java 原生的 TLS 握手层,通过原生的 HTTP/2 客户端裸发请求。

1. 引入依赖

无! 只需要确保你的本地 JDK 版本 $\ge$ 11。

2. Java 实现代码

Java

代码语言:java
复制
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyStore;
import java.time.Duration;

public class RawApnsJavaP12 {
    public static void main(String[] args) {
        // 1. 准备向 iOS 同学索要的【.p12 原材料】
        String p12FilePath = "/path/to/aps_development.p12"; // .p12 证书路径
        String p12Password = "123456";                        // 证书密码
        
        String bundleId = "com.jianding.app";                 // App 的唯一包名
        String deviceToken = "00fc13adff785122b4ad...";       // 目标测试手机的 Token

        // 苹果测试环境 URL 路径
        String apnsUrl = "https://api.sandbox.push.apple.com/3/device/" + deviceToken;

        try {
            // 2. 安全层:将 .p12 证书直接加载进 Java 底层的 TLS 握手上下文中
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            try (FileInputStream fis = new FileInputStream(p12FilePath)) {
                keyStore.load(fis, p12Password.toCharArray());
            }

            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(keyStore, p12Password.toCharArray());

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(kmf.getKeyManagers(), null, null);

            // 3. 协议层:创建强制支持 HTTP/2 的 Java 原生 HttpClient
            HttpClient client = HttpClient.newBuilder()
                    .sslContext(sslContext)              // 注入绑定了证书的安全上下文
                    .version(HttpClient.Version.HTTP_2)  // 强制指定 HTTP/2 协议
                    .connectTimeout(Duration.ofSeconds(10))
                    .build();

            // 4. 数据层与请求构建
            String jsonPayload = "{\"aps\":{\"alert\":{\"title\":\"发货通知\",\"body\":\"纯原生 Java + .p12 发射成功!\"},\"sound\":\"default\"}}";

            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(apnsUrl))
                    .timeout(Duration.ofSeconds(5))
                    .header("apns-topic", bundleId)      // 必填:告诉苹果发给哪个 App
                    .header("apns-push-type", "alert")    // 必填:弹窗类型
                    .POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
                    .build();

            // 5. 发送并处理状态码
            System.out.println("原生 HTTP/2 投递中...");
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 200) {
                System.out.println("【大获成功】苹果响应 200 OK,手机已弹窗!");
            } else {
                System.err.println("【投递失败】错误详情: " + response.body());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

📊 全方位对比

对比维度

方案一:第三方库 (Pushy) + .p8

方案二:纯原生 Java + .p12

外部依赖

需要引入 pushy 及其附带的 netty 依赖(打包体积稍大)

零依赖,全部采用 JDK 11+ 内置类(最干净)

凭证类型

.p8 令牌(Token-based)

.p12 证书(Certificate-based)

证书时效性

永久有效,一劳永逸

只有 1 年有效期,到期不更换线上直接瘫痪

复用灵活性

极高。一个文件能给公司所有 App 发推送

极低。一个证书只能绑定一个指定的 App

环境区分

开发、生产环境通用这一个文件

开发、生产环境必须各自生成独立的 .p12 文件

鉴权发生阶段

应用层 HTTP 请求头。 库在底层把 .p8 算成 JWT 加密字符串,塞进 Authorization 的 Header 里。

传输层 TLS 握手阶段。 Java 建立网络连接的瞬间,通过客户端证书(双向 TLS)直接向苹果自证清白。

连接管理能力

极强。库内置了连接池、多路复用、掉线自动重连。

需要开发者自己利用单例模式去小心维护连接复用。

  • 为什么原生 Java 代码不用 .p8 因为如果用原生 Java 强行去啃 .p8,我们就得用纯原生代码手写一整套 JWT 签名加密算法。为了证明‘我是我’,我们需要写上百行枯燥的签名、时间戳、椭圆曲线加密代码。而 .p12 把身份证明直接融合进了网络长连接的握手阶段,让我们的 HTTP 请求头变得极其干净。

扩展

既然 iOS 系统有苹果官方统一管理的 APNs 统一代收点,那么作为死对头的安卓(Android),当然也有它对应的“APNs”

不过在安卓的世界里,因为历史原因和生态环境,这件事情变得稍微有点“割裂”。

1. 国际标准:FCM(Firebase Cloud Messaging)

在遵循谷歌原生生态的国外,安卓的“APNs”叫做 FCM(旧称 GCM)。

  • 谁运营的:由 Google(谷歌) 官方统一运营。
  • 工作原理:和苹果的 APNs 一模一样。所有的 Android 手机在系统底层都会跟谷歌的服务器维持唯一的一条专属长连接通道。你的后端想给某个安卓手机发通知,也是要把数据 POST 给谷歌的 FCM 接口,再由谷歌推向手机。
  • 效果:哪怕安卓 App 进程被用户杀了,只要手机连着谷歌服务(Google Play Services),系统依然能神奇地弹出通知。

2. 中国特色:手机厂商的“各自为政”

但是,由于国内的安卓手机无法使用谷歌服务(FCM 连不上),国内的安卓推送生态演变成了一场“军阀混战”。

国内的安卓手机没有统一的“一个”APNs,而是每一个手机厂商,都自己做了一个自己的“APNs”

  • 华为手机有:华为推送(Huawei Push)
  • 小米手机有:小米推送(MiPush)
  • OPPO 手机有:OPPO 推送
  • VIVO 手机有:VIVO 推送

🤯 国内后端开发安卓推送的痛苦之处

如果你是一个国内的后端开发,当产品经理和你说:“给我们的安卓版 App 也加上像 iOS 那样的离线系统弹窗吧!”

你不能像 iOS 那样只对接一个苹果就行了,你需要在后端写五六套完全不同的代码:

  1. 先判断这个用户的手机是啥牌子。
  2. 如果是小米,你的后端就要把请求 POST 给小米的服务器
  3. 如果是华为,你的后端就要把请求 POST 给华为的服务器

每一个厂商的 JSON 格式、Header 规范、鉴权方式都完全不一样


3. 第三方中台(极光、个推)的由来

看到这里,你可能已经开始头皮发麻了。为了解决国内安卓开发这种“要给六七个厂商分别写六七套代码”的绝望境地,第三方推送中台(如:极光推送 JPush、个推) 应运而生。

这些中台干了什么事呢?它们在自己的服务器上,把苹果 APNs、谷歌 FCM、华为、小米、OPPO、VIVO 的所有接口全都帮我们封装集成好了

  • 没有中台时(后端要发 6 次请求)你的后端 $\rightarrow$ 分别对接 APNs / FCM / 小米 / 华为 / OPPO / VIVO
  • 有了中台时(后端只需要发 1 次请求)你的后端 $\rightarrow$ 统一发给 极光/个推 $\rightarrow$ 中台自动帮你分发给各个厂商的服务器

无论是苹果的 APNs、谷歌的 FCM、华为鸿蒙的 HMPS,还是国内小米、OPPO、VIVO 各自搞的厂商推送通道,它们在底层的“工作原理”上,相似度高达 99%。所有的不同,仅仅是域名不同、鉴权证书的格式不同、以及 JSON 豆腐块里的 key 改了个名字而已。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、为什么要进行APNs开发?
  • 二、消息推送功能是如何工作的呢?手机上的消息弹窗是谁弄出来的呢?弹窗的具体内容是谁决定的呢?
    • 1.既然是系统负责弹窗,那我们后端、苹果服务器、用户手机之间又是怎么配合的呢?
    • 2.弹窗的具体内容是谁决定的?
  • 三、开发的落地
    • 1.什么时候发送通知?
    • 2. 技术上什么时候触发?(后端的代码埋点)
      • 方式 A:直接调用(同步/异步方法)
      • 方式 B:消息队列(MQ)异步解耦
    • 3. 具体怎么发送消息通知呢? ——APNs 的具体实现
      • (1)APNs 通信的底层原理
      • (2)寄信前的准备:你要向 iOS 同学索要的“四大原材料”
      • (3)官方规定的标准时序流程
      • (四)、 核心:拆解标准的底层 HTTP/2 报文规范
      • (五)、代码实现——p.8验证与p.12验证的对比
    • 🛠️ 方案一:使用第三方库(Pushy)+ .p8 令牌方式
      • 1. 引入依赖 (Maven)
      • 2. Java 实现代码
    • 🛠️ 方案二:使用 Java 11 原生代码 + .p12 证书方式
      • 1. 引入依赖
      • 2. Java 实现代码
    • 扩展
      • 1. 国际标准:FCM(Firebase Cloud Messaging)
      • 2. 中国特色:手机厂商的“各自为政”
      • 🤯 国内后端开发安卓推送的痛苦之处
      • 3. 第三方中台(极光、个推)的由来
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档