Push notification click android

Push уведомления в Android. Грабли, костыли и велосипеды

На написание данной статьи меня подтолкнула задача, которая была поставлена передо мной в одном из рабочих проектов: реализовать Push-уведомления в приложении. Казалось, все просто: штудируешь документацию, примеры и вперед. К тому же, опыт работы с уведомлениями уже был. Но не тут то было…

Сервис, в рамках которого реализовано приложение под Android, предъявляет довольно жесткие требования к работе Push-уведомлений. Необходимо в пределах 30-60 секунд оповестить пользователя о некотором действии. При успешном оповещении с устройства пользователя отправляется запрос на сервер с соответствующим статусом. Из документации известно, что сервис GCM (Google Cloud Messaging) не гарантирует доставку PUSH-уведомлений на устройства, поэтому в качестве backdoor варианта, при нарушении этих временных рамок, наш сервис уведомляет пользователя с помощью SMS сообщения. Поскольку стоимость SMS сообщения существенно выше чем PUSH-уведомления, необходимо максимально сократить поток SMS сообщений на клиентские устройства.

Проштудировав документацию и прикрутив пуш-уведомления, мы разослали нескольким клиентам первую сборку приложения для теста и стали ждать. Результаты были примерно следующими:

  • при активном Wifi соединении все работает идеально: уведомления доставляются, клиенты рады.
  • при активном мобильном интернете началось самое веселье.

Некоторые клиенты писали, что испытывают задержки в доставке пушей, либо получали одновременно и PUSH и SMS, что достаточно не практично. Другие писали, что вовсе не получали уведомлений, а только SMS. У третьих, как и у нас на тестовых устройствах, все было ок. Собрав с недовольных клиентов максимально возможную информацию, стали разбираться в проблеме и вывели следующий список ограничений (этот список позже вылился в полноценный FAQ):

  • включенный режим Энергосбережения (например, Stamina на устройствах Sony) влияет на работу Push уведомлений;
  • у пользователя обязательно должен быть минимум 1 активный Google аккаунт на устройстве;
  • необходимо удостовериться в том, что на устройстве установлена актуальная версия приложения “Сервисы Google Play”;
  • проверить, не отключены ли уведомления для приложения (галочка на страничке приложения в настройках телефона);
  • проверить, не ограничена ли работа фонового режима для приложения (настройка расположена в меню «Использование данных»);
  • в документации к GCM указано, что уведомления рассылаются только по определенным портам, поэтому настройки роутера, файервола и антивируса так же стоит учитывать.

Разослав данную памятку по всем клиентам, мы снова стали ждать результатов. И они оказались снова «не очень». Стали копать дальше.

На данном этапе очень сильно помогла статья, написанная ребятами из Mail.ru. В ней очень подробно описаны тонкости реализации GCM на клиентской стороне, а так же моменты, в связи с которыми отказываются работать Push уведомления в мобильных сетях. В конечном счете было принято решение о том, чтобы держать свое соединение с сервером в связке с GCM.

Перед тем, как приступить к решению, стоить выделить несколько очень важных моментов, которые позволяют сузить круг потенциально «нерабочих» устройств:

  • проблема возникает только при подключении к мобильному интернету;
  • по данным клиентов, проблема возникает на версии андроида 4 и выше.

И так, перейдем к реализации.

Бывалый разработчик под Android сходу скажет, что решений задачи как минимум 2: использовать Service или AlarmManager. Мы попробовали оба варианта. Рассмотрим первый из них.

Для того, чтобы создать неубиваемый системой сервис, который постоянно будет висеть в фоне и выполнять нашу задачу, мы использовали метод:

  • notificationId — некоторый уникальный идентификатор уведомления, который будет выведен в статус баре и в выезжающей шторке;
  • notification — само уведомление.

В данном случае обязательным условием является отображение уведомления в статус баре. Такой подход гарантирует то, что сервису будет дан больший приоритет (поскольку он взаимодействует с UI частью системы) в момент нехватки памяти на устройстве и система будет выгружать его одним из последних. Нам это уведомление не нужно, поэтому мы воспользовались следующим велосипедом: достаточно запустить одновременно с первым сервисом второй и для обоих сервисов в качестве notificationID использовать одно и тоже значение. Затем убить второй сервис. При этом уведомление пропадет из статус бара, но функциональные и приоритетные возможности первого сервиса останутся.

Реализовав данный подход, мы отправили сборку на тест. По результатам выяснилось, что система все-таки выгружает сервис, а по логам мы видели, как происходили существенные временные разрывы при запросе данных в фоне с нашего сервера. Поэтому приступили к реализации второго варианта — AlarmManager.

AlarmManager — это класс, который предоставляет работу с, грубо говоря, «будильником». Он позволяет указать время, по достижении которого система отправит широковещательное уведомление, которое позволит пробудить наше приложение и даст ему возможность выполнить необходимые действия. В работе этого метода есть некоторые ограничения, и их необходимо обработать:

  • данные о «будильниках» будут стерты после перезагрузки устройства;
  • данные о «будильниках» будут стерты после обновления приложения.
Читайте также:  Viber занимает много места андроид

Первыми граблями, на которые мы наступили, был метод

который позволяет установить повторяющийся с некоторым интервалом «будильник». Прикрутив данный способ, стали тестировать, и тесты показали обратное — «будильник» не повторялся. Стали разбираться в чем дело, посмотрели документацию. И именно там нашли ответ на вопрос — начиная с 19 API lvl (Kitkat) абсолютно все «будильники» в системе стали разовыми. Вывод — всегда читайте документацию.

Эти грабли не были поводом для расстройства, ведь решение задачи довольно простое — запускать единоразовый «будильник» и после срабатывания переустанавливать его. При реализации этого подхода мы наткнулись на следующие грабли — оказалось, что для разных уровней API необходимо по разному устанавливать будильники, при этом в документации ничего сказано не было. Но данная проблема решилась достаточно просто — методом «тыка» и «гугления». Ниже представлен пример кода, позволяющий правильно устанавливать «будильники»:

Хочу обратить внимание на флаг AlarmManager.RTC_WAKEUP — именно с помощью него система позволит нашему приложению «проснуться» при неактивном экране, когда устройство находится в заблокированном состоянии.

Данный подход с «будильникам» дал нам нужный результат — приложение в фоне корректно опрашивает сервер на наличие новых данных. Сейчас мы дорабатываем алгоритм. На данный момент реализуем и тестируем следующую оптимизацию, которая позволит сузить круг устройств и тем самым уменьшить нагрузку на сервер:

  • в сообщении, отправленном средствами GCM на устройство, содержится некоторый уникальный ID;
  • получив данные GET запросом в фоновом режиме проверяем, существуют ли уже запись с таким ID на устройстве;
  • если локально на устройстве таких данных нет, мы запоминаем этот ID и время его получения T1;
  • ждем PUSH с таким же ID, при получении запоминаем время T2 и проверяем разницу между T2 и T1;
  • если разница составляет больше некоторого временного критерия (значения), то на устройстве наблюдается проблема с доставкой уведомлений и для корректной работы сервиса необходимо постоянно запрашивать данные в фоновом режиме с сервера (критерий советую выбирать исходя из решаемой задачи. В нашем случае, был выбран критерий равный 5 минутам);
  • данную разницу стоит вычислять несколько раз, например 5-10 раз, только после этого делать вывод о том, что устройство действительно содержит проблему с получением Push уведомлений (таким образом исключается ситуация банального разрыва соединения, таймаута и пр.);
  • необходимо прогонять данный алгоритм периодически (например, раз в неделю, или после обновления ОС на устройстве).

Всем добра. И поменьше подобных костылей.

Источник

How to Get Started With Push Notifications On Android

Introduction

Getting users to install your app is only half the battle. Getting them to use it regularly is the other half. It is quite possible that your users completely forget about your app after using it only once or twice. What with all the other new apps competing for their attention.

By using push notifications, you can remind users about your app every now and then, improving the chances of your app staying installed on their devices.

Google Cloud Messaging, GCM for short, is a free service you can use to send push notifications to your users. In this tutorial, you learn how to use it to create an Android app that can receive push notifications, and a simple server-side Python script that can generate and send them.

Why Use Google Cloud Messaging?

For most client-server communications, the client initiates requests to receive data from the server. In other words, the client pulls data from the server. In the case of push notifications, however, it is the server initiating the data transfer.

This is usually accomplished by maintaining a persistent TCP/IP connection—a connection that stays open indefinitely—between the server and the client. That might sound great, but if you have a popular app, maintaining thousands of persistent connections between your server and the devices of your users can be very expensive.

Google Cloud Messaging is a service that solves this problem by acting as an intermediary between your server and your user’s device. With GCM, Google’s Cloud Connection Server, often referred to as CCS, manages the persistent connections for you. It also makes sure that your push notifications are delivered securely and reliably.

Prerequisites

To follow along with me, you need:

  • the latest version of Android Studio
  • Python 2.7.6 or higher
  • a device running Android 4.4 or higher with Google Play Services installed

1. Setting Up the Android Studio Project

Fire up Android Studio and create a new project with an empty Activity . If you used the defaults, the project should include a Java class in MainActivity.java.

Step 1: Add the Dependencies

In this project, we will be using the Google Services gradle plugin to configure GCM. Include it in the project by adding the following line in the dependencies section of the project’s build.gradle:

Next, apply the plugin in the app module’s build.gradle:

To be able to use the GCM API, add com.google.android.gms:play-services as a compile dependency in the same file:

If you click the Sync Now button, you should see the following error:

Читайте также:  Анимации для смартфона андроид

Click the Install Repository and sync project link to fix the error.

Step 2: Update the Manifest

Inside the project’s AndroidManifest.xml file, create and use a custom C2D_MESSAGE permission based on your project’s package name. Make sure that the protectionLevel of the permission is set to signature.

The notifications are received in the form of broadcasts. To handle those broadcasts, our app needs a BroadcastReceiver . However, we don’t have to create it manually. We can instead use the GcmReceiver class as the BroadcastReceiver .

The BroadcastReceiver must have an intent-filter that responds to the com.google.android.c2dm.intent.RECEIVE action and the name of its category must match your project’s package name. Add the following code to the manifest:

2. Get a Server API Key and a Sender ID

While communicating with the Cloud Connection Server, we need to identify ourselves using an API key on the server side and a sender ID on the client side. To get the API key and the sender ID, create a new project in the developers console.

Start by clicking the Pick a platform button. Next, click the Enable services for my Android App button. When you do so, you will be asked to provide a name and an Android package name for your app. Make sure that the Android package name you provide matches the package name you entered when you created the Android Studio project.

Next, click the Choose and configure services button at the bottom. You can now select the Google services you want to add to the app.

For now, click the Cloud Messaging button and then click Enable Google Cloud Messaging. After a few seconds, you will be presented with your server API key and sender ID. Make a note of the server API key and press Close.

The Google Services plugin we added earlier needs a configuration file to work correctly. Generate the file by clicking the Generate configuration files button.

Once the file has been generated, download it and place it inside your Android Studio project’s app directory.

3. Registering the Client

GCM identifies Android devices using registration tokens. Therefore, our app must be able to register itself from every Android device on which it is installed.

Step 1: Create a Registration Service

The registration must be done on a background thread because the process might take a while depending on network connectivity. Because the registration doesn’t need any inputs from the user, an IntentService is ideal for this task.

Create a new Java class called RegistrationService.java, make it a subclass of IntentService , and override its onHandleIntent method.

Inside the onHandleIntent method, we can use the Instance ID API to generate or fetch the registration token. First, create an instance of the InstanceID class, using its getInstance method.

We can now use the getToken method of the InstanceID object to get the registration token in the form of a String . getToken expects the sender ID as one of its arguments. Because we’ve added the google-services.json file to our project, we can pass the sender ID to the method using R.string.gcm_defaultSenderID .

If you want to see the contents of the registration token for debugging purposes, you can log it as a debug message using the Log.d method.

At this point, you could send the registration token to your web server and store it in a database there. However, you don’t have to do this if you don’t plan to address your users individually. If you plan to send the same message to every user, you should follow the publish-subscribe approach.

I’ll now be showing you how to subscribe to a topic called my_little_topic. It takes just two lines of code. First, create a new instance of the GcmPubSub class using its getInstance method. Next, call its subscribe method and pass the registration token to it, along with the name of the topic.

Our app can now receive every push notification published to my_little_topic.

Finally, define the service in AndroidManifest.xml.

The registration service is complete.

Step 2: Create an InstanceIDListenerService

Registration tokens are refreshed periodically. Consequently, every Android app that uses GCM must have an InstanceIDListenerService that can handle those refreshes. Therefore, create a new Java file called TokenRefreshListenerService.java and make it a subclass of InstanceIDListenerService . Inside the onTokenRefresh method of the class, all we need to do is simply start the registration process again by starting the registration service using an Intent and the startService method.

Add the following code to TokenRefreshListenerService.java:

This service must be able to respond to the com.google.android.gms.iid.InstanceID action. Therefore, while defining the service in AndroidManifest.xml, add the appropriate intent-filter .

Step 3: Starting the Registration Service

To make sure that the registration process begins as soon as the app starts, we must start the RegistrationService class inside the onCreate method of MainActivity . To do so, create an Intent for it and use the startService method.

4. Displaying Push Notifications

GCM automatically displays push notifications in the notification tray as soon as they are received. However, it does so only if the associated app contains a GCMListenerService .

Читайте также:  Nothing to show android studio

Create a new Java class called NotificationsListenerService and make it a subclass of GCMListenerService . Unless you want to handle the pushed data yourself, you don’t have to write any code inside this class. We can leave this class empty for now.

While defining the service in AndroidManifest.xml, make sure that you add an intent-filter that allows it to respond to the com.google.android.c2dm.intent.RECEIVE action.

5. Adding Push Notification Icons

Every push notification must have an icon associated with it. If you don’t have one handy, you can get one from Google’s Material Design Icons Library.

Once you download the icon, place it inside the res folder of your project. I’ll be using ic_cloud_white_48dp as the icon.

6. Running the Android App

Our Android app is now complete. Once you compile it and run it on an Android device, you will be able to see the registration token in the logcat logs.

Press the back button on your device to exit the app. This is necessary because GCM will display push notifications automatically only if the user is not using the app. If you want the notifications to be shown even when the app is running, you will have to create the notifications yourself inside NotificationsListenerService using the Notification.Builder class.

7. Sending Push Notifications

In the final part of this tutorial, we will create a simple Python script that can generate and send push notifications to all the Android devices on which our app is installed.

You can run this script from your local computer or from a remote web server to which you have SSH access.

Step 1: Creating the Script

Create a new file called send.py and open it using your favorite text editor.

At the top of the file, import the urllib2 and urllib modules. We will use these modules to send data to Google’s Cloud Connection Server. Import the json module as well because the data we send must be valid JSON. Lastly, to access command line arguments, import the sys module.

Next, create a variable that stores the server API key you took note of earlier. The key needs to be part of every HTTP request we make to the CCS.

Every notification must have a title and a body. Instead of hard-coding them in our script, let’s accept the title and body as command line arguments using the argv array.

Create a new Python dictionary object to represent the data that should be sent to the CCS. For our Android app to be able to receive the notification, it must be published to a topic called my_little_topic. Therefore, add a key called to to the dictionary and set its value to /topics/my_little_topic.

To represent the contents of the notification, add a key called notification to the dictionary and set its value to another dictionary object containing three keys:

  • body
  • title
  • icon

Make sure that the value of the icon key matches the name of the icon drawable in your Android project.

Convert the dictionary to a JSON string using the dumps function of the json module:

All we need to do now is send the JSON string to https://gcm-http.googleapis.com/gcm/send. To do so, create a new Request object and set dataAsJSON as its data. Next, set the Authorization header to MY_API_KEY and the Content-type header to application/json.

Finally, to execute the request and fetch the response, pass the request object to the urlopen function and call its read method.

The Python script is now complete and ready to use.

Step 2: Running the Script

At this point, we are ready to send push notifications to all the devices on which our app is installed. Open a terminal and enter the directory in which you created send.py.

Pass the name of the script to the python executable along with a string for the title of the notification and one for the notification’s body. Here’s an example you can use:

If there are no errors, you should get a response that looks like this:

If you check your Android device, you should see a new notification in the notification tray.

Conclusion

You now know how to send push notifications to your users. In this lesson, you learned how to create an Android app capable of registering itself, and receiving notifications that are published to a specific topic. You also learned how to create a Python script that can publish notifications.

Even though push notifications may feel like a great way to communicate with your users, I suggest that you use them sparingly and only if you have something useful to say, because sending too many of them too often is perhaps the quickest way to get your app uninstalled.

To learn more about Google Cloud Messaging, refer to the Cloud Messaging Guide.

Источник

Оцените статью