Статьи

iOS и Android Push-уведомления от Java

В течение нескольких последних недель я работал техническим архитектором в мобильном приложении для крупного банка (или, я бы сказал, в отделе страхования этого банка). Приложение было написано в PhoneGap и Sencha Touch. Поддерживались две платформы: iOS и Android. Я отвечал (помимо всего прочего) за разработку уведомлений для iOS и Android. Уведомление просто сообщило страховому агенту, сколько случаев и действий было обновлено / изменено.

Отправка уведомлений на устройства iOS

Чтобы использовать Apple Push Notification Service (APNS), помимо стандартного сертификата разработчика и действительного профиля обеспечения, вам нужна еще одна вещь. Вам необходим сертификат APNS, который используется для взаимной авторизации. Вы можете сгенерировать его на портале Apple для обеспечения iOS. Как только он у вас есть, вы готовы отправлять уведомления.

APNS использует двоичный протокол. Конечно, вам не нужно реализовывать это с нуля. Вы можете использовать очень хорошую и тщательно протестированную библиотеку notnoop java-apns, которая доступна здесь: https://github.com/notnoop/java-apns .

Использовать его чрезвычайно просто, следующий код работает нормально ( Notificationэто мой внутренний класс):

@Service
@Qualifier("Apns")
public class ApnsPushNotificationServiceImpl implements PushNotificationService {
 private Resource certResource;
 private String certPassword;
 private boolean useProductionServer;
 private String serverAddress;
 private static final int DEFAULT_APNS_PORT = 2195;
 public void setCertResource(Resource certResource) {
  this.certResource = certResource;
 }
 public void setCertPassword(String certPassword) {
  this.certPassword = certPassword;
 }
 public void setUseProductionServer(boolean useProductionServer) {
  this.useProductionServer = useProductionServer;
 }
 public void setServerAddress(String serverAddress) {
  this.serverAddress = serverAddress;
 }
 public void pushNotification(Notification notification) {
  List<Notification> notList = new ArrayList<Notification>();
  notList.add(notification);
  pushNotifications(notList);
 }
 public void pushNotifications(List<Notification> notifications) {
  ApnsService service = createApnsService();
  service.start();
  for (Notification notification : notifications) {
   doSendNotification(notification, service);
  }
  service.stop();
 }
 private ApnsService createApnsService() {
  ApnsService service = null;
  ApnsServiceBuilder serviceBuilder = APNS.newService()
    .withCert(certResource.getInputStream(), certPassword);
  if (null != serverAddress && serverAddress.length() > 0) {
   serviceBuilder.withGatewayDestination(serverAddress,
     DEFAULT_APNS_PORT);
  } else if (useProductionServer) {
   serviceBuilder.withProductionDestination();
  } else {
   serviceBuilder.withSandboxDestination();
  }
  service = serviceBuilder.build();
  return service;
 }
 private void doSendNotification(Notification notification,
   ApnsService service) {
  PayloadBuilder payloadBuilder = APNS.newPayload();
  payloadBuilder = payloadBuilder.badge(notification.getBadge());
  payloadBuilder = payloadBuilder.sound(notification.getSound());
  if (notification.getBody() != null) {
   payloadBuilder = payloadBuilder.alertBody(notification.getBody());
  }
  String payload = payloadBuilder.build();
  service.push(notification.getDeviceToken(), payload);
 }

Отправка уведомлений на устройства Android

По сравнению с уведомлениями iOS реализация обмена сообщениями устройств Android Cloud 2 (AC2DM) была простой задачей. Мне не нужна третья библиотека. Благодаря простым и понятным сервисам Google RESTful я смог написать код, отвечающий за передачу уведомлений на устройства Android менее чем за час.

Весь процесс состоит из 2 этапов. Первый — аутентификация, второй — фактическое нажатие уведомления. Исходный код выглядит следующим образом:

@Service
@Qualifier("Ac2dm")
public class Ac2dmPushNotificationServiceImpl implements PushNotificationService {
 private String sendingRoleAccount;
 private String sendingRolePassword;
 public void pushNotification(Notification notification) {
  List<Notification> notList = new ArrayList<Notification<();
  notList.add(notification);
  pushNotifications(notList);
 }
 public void pushNotifications(List<Notification> notifications) {
  try {
   HttpClient client = new HttpClient();
   PostMethod method = new PostMethod(
     "https://www.google.com/accounts/ClientLogin");
   method.addParameter("Email", sendingRoleAccount);
   method.addParameter("Passwd", sendingRolePassword);
   method.addParameter("accountType", "HOSTED_OR_GOOGLE");
   method.addParameter("source", "unit-test");
   method.addParameter("service", "ac2dm");
   client.executeMethod(method);
   byte[] responseBody = method.getResponseBody();
   String response = new String(responseBody);
   String auth = response.split("\n")[2];
   String token = auth.split("=")[1];
   for (Notification notification : notifications) {
    doSendNotification(notification, token, client);
   }
  } catch (Throwable t) {
   throw new RuntimeException(t);
  }
 }
 private void doSendNotification(Notification notification, String token, HttpClient client) throws HttpException, IOException {
  PostMethod method = new PostMethod("https://android.apis.google.com/c2dm/send");
  method.addParameter("registration_id", notification.getDeviceToken());
  method.addParameter("collapse_key", "collapse");
  method.addParameter("data.payload", notification.getBadge().toString());  
  Header header = new Header("Authorization", "GoogleLogin auth="+token);
  method.addRequestHeader(header);
  client.executeMethod(method);  
  byte[] responseBody = method.getResponseBody();  
  String response = new String(responseBody);
 }
 public void setSendingRoleAccount(String sendingRoleAccount) {
  this.sendingRoleAccount = sendingRoleAccount;
 }
 public void setSendingRolePassword(String sendingRolePassword) {
  this.sendingRolePassword = sendingRolePassword;
 }

Замечание по WebSphere и IBM Java 5

Когда приложение было развернуто на сервере UAT, и мы попытались запустить интеграционный тест, отвечающий за отправку уведомлений на один из наших тестовых iPhone, мы увидели сообщение «java.security.InvalidKeyException: Illegal key size» в журнал. После многих часов исследований выяснилось, что IBM Java 5 поддерживает только 512 размеров ключей. Нам пришлось обновить jav-файлы безопасности Java 5 IBM, чтобы он читал сертификат APNS.

-1 для IBM!

Резюме

Это заняло у меня некоторое время, но когда вы видите уведомления, приходящие на ваше устройство, а затем с помощью PhoneGap вы запускаете приложение JavaScript, которое выглядит как нативное приложение, и вы чувствуете себя хорошо 🙂

ура,
Лукаш