iOS消息推送APNS(Java实现HTTP/2协议发送)

 首先来了解一下苹果的消息推送APNS(英文全称:Apple Push Notification service)

先来看两张苹果对于推送的两张解释图:

大概的意思就是,提供商把消息推送至苹果的推送服务器,再由苹果推送服务器将消息推送给手机客户端,或者反过来手机客户端把消息推送至苹果推送服务器,再由苹果推送服务将消息推送至提供商。这里提到的提供商可以是像极光推送或其他供应商,亦或是自己的服务器。

这里提到了苹果推送服务器向手机端推送消息,那么它是怎么知道是推送到哪个手机上的呢,这个就汲及到一个Token的问题。

Token是每台iOS手机唯一的标识,在启动手机应用时,会向苹果推送服务器去请求得到唯一的Token。当然,虽然每次应用启动时都会去请求得到Token,但是针对于同一部手机,返回回来的Token值都是一致的。

所以当苹果推送服务器只要知道要推送给哪个Token即可知道是推送至哪个手机。

接下来看一下APNS如何去实现消息的推送

1.首先是要准备好证书

从你的苹果电脑上导出CSR文件,打开钥匙串–>钥匙串访问–>证书助理–>从证书颁发机构请求证书,然后一路输入邮件地址和保存的文件,保存到电脑,CSR文件就生成了。

到苹果开发者官网http://developer.apple.com,登陆进入到证书管理模块,Identifiers –> App IDs, 如果你的应用ID还没有添加进来,那么点右上角加号添加进来,如果已经加进来了,单击进入,进行设置,将Push Notifications 设成Enable.

在Certificates创建推送证书,生成后下载双击安装到电脑。这篇文章写的是HTTP/2无证书发送,所以不需要导出p12给服务器去发消息,只需要安装到mac就可以了。

由于推送需要用到真机,所以还需要Provisioning Profiles 证书,生成后也要在xcode中设置好。

2.给应用注册推送服务

  • 首先在项目的AppDelegate.m中加入以下两个代理方法
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { 
    NSString *token = [NSString stringWithFormat:@"%@", deviceToken];
    //获取终端设备标识,这个标识需要通过接口发送到服务器端,服务器端推送消息到APNS时需要知道终端的标识,APNS通过注册的终端标识找到终端设备。
    NSLog(@"My token is:%@", token);   
}  

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {   
    NSString *error_str = [NSString stringWithFormat: @"%@", error];   
    NSLog(@"Failed to get token, error:%@", error_str);   
}

  • 在AppDelegate.m的(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中加入注册消息通知推送能力;加入当应用程序处于未启动状态时,判断是否由远程消息通知触发;加入清除消息推送通知标记。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  //判断是否由远程消息通知触发应用程序启动
    if ([launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]!=nil) {
        //获取应用程序消息通知标记数(即小红圈中的数字)
        int badge = [UIApplication sharedApplication].applicationIconBadgeNumber;
        if (badge>0) {
           //如果应用程序消息通知标记数(即小红圈中的数字)大于0,清除标记。
            badge--;
          //清除标记。清除小红圈中数字,小红圈中数字为0,小红圈才会消除。
            [UIApplication sharedApplication].applicationIconBadgeNumber = badge;
        }
    }
    //消息推送注册
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeSound|UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeBadge];
}

  • 在项目AppDelegate.m中加入消息接收处理代理方法。

//处理收到的消息推送
- (void)application:(UIApplication *)application 
didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    //在此处理接收到的消息。
    NSLog(@"Receive remote notification : %@",userInfo);
}

3.Java后台苹果推送服务器推送消息(HTTP/2协议)

苹果官方自2021年开始就建议使用HTTP/2协议来发消息到苹果的推送服务器,我是2022年接到有客户的请求收不到信息了才去研究原来之前的发消息方法已经不能再用了,重新研究并写下JAVA发HTTP/2的方法。特别注意,需要 JDK 11 及以上版本才支持HTTP/2的协议,如果低于这个版本的朋友们请赶紧升级JDK吧。

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;

public class ApnsSendUtil {
	
	private static String APPID = "com.renhxyy.erp";
	
	private static String SANDBOX_SERVER = "api.sandbox.push.apple.com:443";
	private static String PRODUCT_SERVER = "api.push.apple.com:443";
	
	
	public static void main(String[] args) throws IOException, InterruptedException, InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, SignatureException {
		// 消息,参考:https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification
		String payload = "{ \"aps\" : { \"alert\" : \"邹宝珠的测试消息!\" } }";
		Integer statuCode = ApnsSendUtil.send("f215ffebd0b4cac54a6d944a498c209deb5b58b789fb26da08bbb30af8dc8440", false, payload);
		System.out.println(statuCode);
	}
	
	public static Integer send(String deviceToken, Boolean isProduct, String payload) 
			throws IOException, InterruptedException, InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, SignatureException {
		
		HttpClient client = HttpClient.newBuilder().build();

		HttpRequest request = HttpRequest.newBuilder()
				.uri(URI.create("https://" + (isProduct ? PRODUCT_SERVER : SANDBOX_SERVER) +"/3/device/" + deviceToken)) // deviceToken 从手机上获取到的设备 token
				.POST(BodyPublishers.ofString(payload)) // 发送的消息内容
				.setHeader("authorization", JWT.getToken()) // 请苹果接口的令牌,通过 JWT 生成,有时间有过期(好像是一个小时过期,所以不要去缓存它,每次请求时去重新生成一个就好)
				.setHeader("apns-id", "eabeae54-14a8-11e5-b60b-1697f925ec7a") // 可以理解成APP名称吧,符合格式即可,随意命名
				.setHeader("apns-push-type", "alert") // 消息的提示方式,alert 是指弹出横幅
				.setHeader("apns-expiration", "0")
				.setHeader("apns-priority", "10") // 10 表示立即发送
				.setHeader("apns-topic", APPID) //  APPID,和证书一致
				.build();

		HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());

		Integer statuCode = response.statusCode(); // 返回状态码,如果是 200 则表示发送成功

		if (statuCode == null || statuCode.intValue() != 200) {
			System.out.println(response.body()); // 如果返回的状态码不是200,则发送失败,response.body() 是错误的原因
		}
		
		return statuCode;
	}
	
	
	
	

}

可以参考苹果官方文档说明:

Apple Developer Documentation

附上 JAVA HTTP/2协议请求APNS的完整代码

iOS消息推送(Java实现HTTP/2协议发送APNS)-Java文档类资源-CSDN下载

效果图

备注:这篇文章没有对证书和APP端代码(即第1步和第2步)做详细的描述,网上有很多文章对这部分有详解,如果还不懂可以加我微信 kerryzb 聊。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注