Developers Club geek daily blog

1 year, 8 months ago
On writing of this article I was pushed by a task which was set for me in one of detail designs: to implement Push-notifications in the application. It seemed, everything is simple: you study documentation, examples and forward. Besides, experience with notifications already was. But far from it …

Service within which the Android application is implemented imposes quite strict requirements to work of Push-notifications. It is necessary to notify within 30-60 seconds the user on some action. At the successful notification from the device of the user a request for the server with the corresponding status is sent. From documentation it is known that the GCM service (Google Cloud Messaging) does not guarantee delivery of PUSH notifications to devices therefore as option backdoor, at violation of these time frames, our service notifies the user by means of the Sms. As the cost of the Sms is significantly higher than PUSH notifications, it is necessary to reduce a flow of Sms by client devices as much as possible.

Having studied documentation and having fastened push-notifications, we sent to several clients the first assembly of the application for the test and began to wait. Results were approximately following:

  • at active Wifi connection everything works ideally: notifications are delivered, clients are glad.
  • at the active mobile Internet the fun began.

Some clients wrote that they test delays in delivery greater, or received at the same time and PUSH and the SMS that is rather not practical. Others wrote that they did not receive at all notifications but only the SMS. The third, as well as at us on test devices, had everything ok. Having collected the greatest possible information from dissatisfied clients, began to understand a problem and output the following list of restrictions (this list poured out in full-fledged FAQ later):

  • the included Energy saving mode (for example, Stamina on devices of Sony) influences work of Push of notifications;
  • the user shall have at least 1 active Google the account on the device;
  • it is necessary to make sure that on the device the actual application version "Play Google services" is set;
  • to check whether notifications for the application are disconnected (a tick on an application page in settings of phone);
  • to check whether work of the background mode for the application is limited (setup is located in the Use of Data menu);
  • in documentation to GCM it is specified that notices are sent only on certain ports therefore settings of a router, a firewall and an antivirus it is also worth considering.

Having sent this instruction on all clients, we began to wait for results again. And they appeared again "not really". Began to dig further.

At this stage article written by children from Mail.ru very strongly helped. In it subtleties of implementation of GCM on client side, and also the moments in connection with which notification Push in mobile networks refuse to work are very in detail described. Eventually the decision on holding the connection with the server together with GCM was made.

Before starting a solution, to be necessary to select some of very important points which allow to narrow a circle of potentially "non-working" devices:

  • the problem arises only at connection to the mobile Internet;
  • according to clients, the problem arises on the version of the android 4 and above.

And so, we will pass to implementation.

The skilled developer under Android will tell a descent that solutions of a task at least 2: to use Service or AlarmManager. We tried both options. Let's consider the first of them.

To create the service which is not killed with system which will constantly hang in a background and to carry out our task, we used a method:

startForeground(int notificationID, Notification notification);

where

  • notificationId — some unique identifier of the notification which will be displayed in the status bar and in the leaving blind;
  • notification — the notification.

In this case an indispensable condition is display of the notification in the status bar. Such approach guarantees that service will be given a bigger priority (as it interacts with UI part of system) at the moment of memory contention on the device and the system will unload its one of the last. We do not need this notification therefore we used the following bicycle: it is enough to start along with the first service of the second and for both services as notificationID to use one and too value. Then to kill the second service. At the same time the notification will be gone from the status of bar, but functional and priority opportunities of the first service will remain.

Having implemented this approach, we sent assembly to the test. By results it became clear that the system after all unloads service, and on logs we saw how there were essential temporary gaps at request of data in a background from our server. Therefore started implementation of the second option — AlarmManager.

AlarmManager is a class which employs with, roughly speaking, "alarm clock". He allows to specify time on reaching which the system will send the broadcasting notification which will allow to awaken our application and will give it the chance to perform necessary operations. In work of this method there are some restrictions, and they need to be processed:

  • data on "alarm clocks" will be erased after reset of the device;
  • data on "alarm clocks" will be erased after updating of the application.

The method was the first rake on which we stepped

setRepeating()

which allows to set the "alarm clock" repeating with some interval. Having fastened this method, began to test, and tests showed the return — "alarm clock" did not repeat. Began to understand what business, looked at documentation. And exactly there found the answer to a question — since 19 API lvl (Kitkat) all "alarm clocks" in system became one-time. An output — always read documentation.

This rake was not a reason for frustration, a task solution quite simple — to start one-time "alarm clock" and after operation to reinstall it. At implementation of this approach we came across the following rake — it turned out that for the different API levels it is necessary to set alarm clocks on a miscellaneous, at the same time in documentation nothing was told. But this problem was solved rather simply — the "tyka" and "gugleniye" method. The code sample allowing to set correctly "alarm clocks" is given below:

private static void setUpAlarm(final Context context, final Intent intent, final int timeInterval)
{
    final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    final PendingIntent pi = PendingIntent.getBroadcast(context, timeInterval, intent, 0);
    am.cancel(pi);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
    {
        final AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(System.currentTimeMillis() + timeInterval, pi);
        am.setAlarmClock(alarmClockInfo, pi);
    }
    else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
        am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeInterval, pi);
    else
        am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeInterval, pi);
}

I want to pay attention to AlarmManager.RTC_WAKEUP flag — by means of it the system will allow our wake up application at the inactive screen when the device is in the blocked status.

This approach with "alarm clocks" yielded to us the necessary result — the application in a background correctly polls the server on existence of new data. Now we finish algorithm. At the moment we implement and we test the following optimization which will allow to narrow a circle of devices and by that to reduce load of the server:

  • the message sent by funds of GCM for the device contains some unique ID;
  • having obtained data of GET request in the background we check whether exist already record with such ID on the device;
  • if locally on the device there are no such data, we remember this ID and time of its receipt of T1;
  • we wait for PUSH with the same ID, when obtaining we remember time of T2 and we check a difference between T2 and T1;
  • if the difference makes more than some temporary criterion (value), then on the device the problem with delivery of notifications is observed and for correct work of service it is necessary to request constantly data in the background from the server (criterion I advise to select proceeding from a solvable task. In our case, the criterion was selected to equal 5 minutes);
  • this difference should be calculated several times, for example 5-10 times, only after that to draw a conclusion that the device really contains a problem with receipt of Push of notifications (thus the situation of a banal rupture of connection, a timeout and so forth is excluded);
  • it is necessary to banish this algorithm periodically (for example, weekly, or after the OS updating on the device).

All she is kind. Less similar crutches.

P.S.
In the course of testing the tool which gives the chance to look at information on the sent dense forests very much helped. This tool is available to developers free of charge. I recommend to all it to use.

P.S.S.
I predict, in comments for certain there will be questions of a battery expense. I carried out several tests, having left personal phone for the night with the included mobile Internet. Results were around 20-25% of an expense of a charge in 8-9 hours. Also clients to whom we sent test assemblies, did not complain of problems with increase in an expense of battery power.

This article is a translation of the original post at habrahabr.ru/post/274169/
If you have any questions regarding the material covered in the article above, please, contact the original author of the post.
If you have any complaints about this article or you want this article to be deleted, please, drop an email here: sysmagazine.com@gmail.com.

We believe that the knowledge, which is available at the most popular Russian IT blog habrahabr.ru, should be accessed by everyone, even though it is poorly translated.
Shared knowledge makes the world better.
Best wishes.

comments powered by Disqus