Поиск по этому блогу

воскресенье, 9 июня 2013 г.

Отправка Push Notifications на iOS с Google App Engine при помощи библиотеки java-apns

Задача отправки уведомлений (Push Notifications) на iOS-устройства (iPhone, iPad и iPod Touch)
Наше web-приложение работает на сервере Google App Engine (GAE). До версии GAE 1.7.7 отправка уведомлений на iOS было проблемой, потому что оно осуществляется через сокеты которые стали доступны только с версии 1.7.7 .  Теперь можно отправлять используя сокеты. Для отправки необходимо подключить к проекту библиотеку java-apns (библиотека доступна на github https://github.com/notnoop/java-apns)

Maven dependency для подключения библиотеки к проекту
https://github.com/notnoop/java-apns/wiki/Installation
http://mvnrepository.com/artifact/com.notnoop.apns/apns/0.2.3

Для реализации отправки уведомлений на iOS необходимо сделать следующее 

1. Метод, который будет регистрировать iOS устройство
2. Метод, который будет удалять регистрационную ифнормацию об iOS-устройстве
3. Метод, который будет отправлять push notifications на мобильное устройство


Метод для регистрации iOS-устройства на сервере


deviceToken который будет присылаться с iOS-устройства пользователя (например при логине в приложение)
deviceToken - это UDID – это уникальный идентификатор устройства, состоящий из 40 символов. Он уникален у каждого iPhone, iPad или iPod Touch, как паспорт. У пользователя есть поле deviceToken без него уведомления не смогут отправляться

@GET
@Path("/regIosDevice")
@Produces({ MediaType.APPLICATION_JSON })
public Response regIosDevice(@QueryParam("deviceToken") String deviceToken,
  @QueryParam("userId") Long userId) {
 
 User user = manager.retrieve(userId);
 if (user != null && !deviceToken.equals(user.getDeviceToken())) {
  user.setDeviceToken(deviceToken);
  manager.save(user);
 }
 return Response.ok().build();
 
}


Метод для удаления зарегистрированного iOS-устройства


Например, при выходе из приложения у пользователя удаляется deviceToken, чтобы если пользователь вышел из приложения, то ему не присылались уведомления

@GET
@Path("/unregIosDevice")
@Produces({ MediaType.APPLICATION_JSON })
public Response unregIosDevice(@QueryParam("deviceToken") String deviceToken,
  @QueryParam("userId") Long userId) {
 
 if (deviceToken != null && !deviceToken.isEmpty()) {
  User user = manager.retrieve(userId);
  if (user != null) {
   user.setDeviceToken(null);
   manager.save(user);
  }
 }
 return Response.ok().build();
 
}

Методы для очереди


С помощью классов gae создаем очередь задач, в которую будут добавляться задачи отправки уведомлений пользователям

В ресурсе пишем

@POST
@Path("/createCargo")
@Consumes(MediaType.APPLICATION_JSON)
@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN })
public Response createCargo(CargoDTO entity) {
 
        ...         
 
 if (manager.isUserConfirmedEmailAndPhone(Long.parseLong(userId))) {
  cargoManager.sendPushNotification(edited);
  
 }
 
 ...
 
}

В менеджере пишем

public void sendPushNotification(Cargo created) {
 
    Queue queue = QueueFactory.getDefaultQueue();
    queue.add(TaskOptions.Builder.withRetryOptions(
 RetryOptions.Builder.withTaskRetryLimit(3)).payload(
  new NewCargoPushNotificationsTask(created)));
 
}

Метод для отправки Push Notifications на iOS-устройство


Создадим класс, в который вынесем метод общий для всех других классов. Метод  будет отправлять notification на iOS-устройство. Если у пользователя есть deviceToken, то ему будет отправляться сообщение. Создаем тело уведомления (payload) и отправляем уведомление пользователю с помощью метода push

PayloadBuilder payloadBuilder = APNS.newPayload();
 
payloadBuilder.alertBody(message);
 
apnsService.push(deviceToken, payloadBuilder.build());


public abstract class BaseSendPushNotificationTask implements DeferredTask {
 
 private static final Logger log = Logger
  .getLogger(BaseSendPushNotificationTask.class.getName());
 
 private static final long serialVersionUID = 4666909051310909540L;
 
 protected void sendMessageToIos(ApnsService apnsService, 
          User user, String message) {
 
            if (user.getDeviceToken() != null) {
 
  String deviceToken = user.getDeviceToken();
 
  try {
 
   PayloadBuilder payloadBuilder = APNS.newPayload();
 
   payloadBuilder.alertBody(message);
 
   apnsService.push(deviceToken, payloadBuilder.build());
 
  } catch (Exception e) {
 
   log.log(Level.WARNING, e.getMessage(), e);
 
  }
   }
      }
}


Создадим класс наследник 


Для этого понадобится сгенерированный на mac файл сертификата с расширением .p12 и пароль к нему На основании этого файла создаем объект ApnsServiceBuilder

String certFilePath = url.getFile();
 
String certPassword = "your password here";
 
ApnsServiceBuilder serviceBuilder = APNS.newService()
  .withCert(certFilePath, certPassword)
  .withNoErrorDetection();

Для тестирования с сертификатом разработчика необходимо
использовать

serviceBuilder.withSandboxDestination();

Для тестирования в режиме Ad Hoc и для продакш релиза надо использовать

serviceBuilder.withProductionDestination();

Создаем объект класса ApnsService

apnsService = serviceBuilder.build();

здесь вызываем метод родительского, класса в который передаем apns service пользователя и сообщение

После того как уведомления отправлены всем пользователям можно остановить сервис

apnsService.stop();

public class NewCargoPushNotificationsTask extends
  BaseSendPushNotificationTask {
 
 private static final long serialVersionUID = 7979661993088302683L;
 
 private Cargo cargo;
 private ApnsService apnsService;
 
 public NewCargoPushNotificationsTask(Cargo cargo) {
  super();
  this.cargo = cargo;
 }
 
 @Override
 public void run() {
 
  // get cargoDto
  ...
 
  // send push notifications
  // service for push notification to ios
  URL url = this.getClass()
                  .getResource("/path to apple certificate file .p12");
 
  String certFilePath = url.getFile();
 
  String certPassword = "your password here";
 
  ApnsServiceBuilder serviceBuilder = APNS.newService()
    .withCert(certFilePath, certPassword)
                  .withNoErrorDetection();
 
  // for production and for ad hoc testing
  serviceBuilder.withProductionDestination();
 
  // don't delete it's for test on device
  // serviceBuilder.withSandboxDestination();
 
  apnsService = serviceBuilder.build();
  String message = "Новые заявки на перевозку";
 
  List<User> users = UserManager.getInstance()
   .retrieveDriversByTruckCategoryAndLocation(
    cargo.getTruckCategory(), 
    cargo.getDispatchLocation());
 
  for (User user : users) {
   sendMessageToIos(apnsService, user, message);
  }
 
  if (apnsService != null) {
   apnsService.stop();
  }
 
 }
}

Комментариев нет:

Отправить комментарий