一、命令行粗略走一下流程
获取需要的配置信息
开发者平台:
idmsa.apple.com/IDMSWebAuth...
这里能拿到发送消息需要的各种配置信息 只有管理员才能登陆查看 我们使用token方式推送,这里只列举该方式需要的配置
js
push.ios.teamId = ##teamId##
push.ios.keyId = ##keyid##
push.ios.authKey.path = /home/key/AuthKey.p8
push.ios.topic = ##topic##
命令行验证消息发送
官方文档
developer.apple.com/documentati...
执行命令
js
## 配置信息
TEAM_ID=##teamid##
TOKEN_KEY_FILE_NAME=/home/key/AuthKey.p8
AUTH_KEY_ID=##keyid##
TOPIC=##topic##
DEVICE_TOKEN=##设备id##
APNS_HOST_NAME=api.push.apple.com
## 生成请求需要的参数
JWT_ISSUE_TIME=$(date +%s)
JWT_HEADER=$(printf '{ "alg": "ES256", "kid": "%s" }' "${AUTH_KEY_ID}" | openssl base64 -e -A | tr -- '+/' '-_' | tr -d =)
JWT_CLAIMS=$(printf '{ "iss": "%s", "iat": %d }' "${TEAM_ID}" "${JWT_ISSUE_TIME}" | openssl base64 -e -A | tr -- '+/' '-_' | tr -d =)
JWT_HEADER_CLAIMS="${JWT_HEADER}.${JWT_CLAIMS}"
JWT_SIGNED_HEADER_CLAIMS=$(printf "${JWT_HEADER_CLAIMS}" | openssl dgst -binary -sha256 -sign "${TOKEN_KEY_FILE_NAME}" | openssl base64 -e -A | tr -- '+/' '-_' | tr -d =)
AUTHENTICATION_TOKEN="${JWT_HEADER}.${JWT_CLAIMS}.${JWT_SIGNED_HEADER_CLAIMS}"
## 发起http2请求
curl -v --header "apns-topic: $TOPIC" --header "apns-push-type: alert" --header "authorization: bearer $AUTHENTICATION_TOKEN" --data '{"aps":{"alert":"test"}}' --http2 https://${APNS_HOST_NAME}/3/device/${DEVICE_TOKEN}
如下是一个调用成功的返回,并且app应该收到了推送消息。

经过上面的步骤就能确认自己的配置信息及环境是否有问题了。
二、编码实现
官方的发送方式有证书和令牌两种,我们选择了令牌的方式 文档如下:
使用令牌发送推送通知
选择的框架:pushy
pushy底层发起http2请求到apple服务器使用的netty.
注意:依赖的jar包如果冲突,大概率会导致发送消息失败,我们这里是直接将其他项目依赖的netty包都排除掉,使用的pushy自己的的netty包。
核心代码
pom.xml
js
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy</artifactId>
<version>0.15.4</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>2.0.62.Final</version>
<scope>runtime</scope>
</dependency>
IosPushHandlerFactory 生成apnsclient
js
new ApnsClientBuilder()
.setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
.setSigningKey(ApnsSigningKey.loadFromPkcs8File(new File(getAuthKeyPath()),
getTeamId(), getKeyId()))
.build();
IosPushHandler 构建请求包及发起推送请求
js
public SimpleApnsPushNotification buildPayLoad(AppPushRecord record, AppUserToken userToken) {
if (!PhoneType.IPHONE.getModel().equals(userToken.getPhoneType()) && !PhoneType.IPAD.getModel().equals(userToken.getPhoneType())) {
throw new ServiceException("设备类型为" + userToken.getPhoneType() + ",不能使用ios推送!");
}
Integer unReadCount = userToken.getUnReadCount();
if (unReadCount == null || (unReadCount != null && unReadCount.intValue() < 0)) {
unReadCount = 0;
}
HashMap<String, Object> userInfo = new HashMap<String, Object>() {{
put("type", record.getType());
put("minorType", record.getMinorType());
put("params", record.getParams());
put("imgUrl", record.getImgUrl());
put("recordId", record.getId());
}};
final ApnsPayloadBuilder payloadBuilder = new SimpleApnsPayloadBuilder();
payloadBuilder.setAlertBody(record.getMessage())
.setAlertTitle(record.getTitle())
.setSound("default")
.setBadgeNumber(unReadCount)
.setAttributes(userInfo)
.build();
final String payload = payloadBuilder.build();
SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(userToken.getDeviceToken(), topic, payload);
logger.info(String.format("push to %d : %s", userToken.getUserId(), JSON.toJSONString(pushNotification)));
return pushNotification;
}
js
public PushNotificationResponse toPush(SimpleApnsPushNotification n, String dvcToken) {
try {
PushNotificationFuture<SimpleApnsPushNotification, PushNotificationResponse<SimpleApnsPushNotification>>
sendNotificationFuture = service.sendNotification(n);
PushNotificationResponse<SimpleApnsPushNotification> pushNotificationResponse =
sendNotificationFuture.get(30, TimeUnit.SECONDS);
logger.info("NotificationResponse result= " + new Gson().toJson(pushNotificationResponse));
if (pushNotificationResponse.isAccepted()) {
logger.info("ios push success");
} else {
logger.info("ios push failure");
}
return pushNotificationResponse;
} catch (TimeoutException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
} finally {
}
问题排查
1、验证网络问题
不要 ping api.push.apple.com
可以 curl api.push.apple.com/ 返回
js
{
"reason": "MethodNotAllowed"
}
2、如果使用命令行调用没问题,调用push接口返回类似shakehande ,socket报错,大概率是jar冲突