📜 ⬆️ ⬇️

Push notifications in android. Rakes, crutches and bicycles

To write this article I was pushed by the task that was set before me in one of the working projects: to implement Push-notifications in the application. Everything seemed simple: you study the documentation, examples and go ahead. In addition, the experience with notifications has already been. But it was not there…

The service, in which the application is implemented under Android, makes quite stringent requirements for the work of Push-notifications. It is necessary within 30-60 seconds to notify the user about some action. If the notification is successful, a request is sent from the user's device to the server with the corresponding status. It is known from the documentation that the GCM service (Google Cloud Messaging) does not guarantee the delivery of PUSH notifications to devices, therefore, as a backdoor option, if this time frame is violated, our service notifies the user via SMS. Since the cost of an SMS message is significantly higher than push-notifications, it is necessary to minimize the flow of SMS messages to client devices.

After reviewing the documentation and screwing up push-notifications, we sent the first application build for the test to several clients and waited. The results were similar to the following:


Some customers wrote that they were experiencing delays in the delivery of push, or received both PUSH and SMS at the same time, which is not practical enough. Others wrote that they did not receive any notification at all, but only SMS. At the third, as well as at us on test devices, everything was ok. Having collected the maximum possible information from dissatisfied customers, they began to understand the problem and brought out the following list of restrictions (this list later turned into a full-fledged FAQ):
')

Having sent this memo to all customers, we again began to wait for results. And they were again "not very." Began to dig further.

At this stage, the article written by the guys from Mail.ru really helped. It describes in detail the subtleties of the GCM implementation on the client side, as well as moments in connection with which Push notifications in mobile networks refuse to work. Ultimately, it was decided to keep its connection to the server in conjunction with the GCM.

Before proceeding to the solution, it is worthwhile to highlight a few very important points that allow us to narrow the circle of potentially "non-working" devices:


And so, we proceed to the implementation.

An experienced Android developer will immediately say that at least 2 solutions to the problem: use Service or AlarmManager. We tried both options. Consider the first one.

In order to create a service not killed by the system, which will constantly hang in the background and perform our task, we used the method:

startForeground(int notificationID, Notification notification); 

Where


In this case, a prerequisite is to display a notification in the status bar. This approach ensures that the service will be given higher priority (since it interacts with the UI part of the system) at the time of low memory on the device and the system will unload it with one of the last. We do not need this notification, so we used the following bike: it is enough to run the second one simultaneously with the first service and use the same value as the notificationID for both services. Then kill the second service. At the same time, the notification will disappear from the status of the bar, but the functional and priority features of the first service will remain.

Having implemented this approach, we sent the assembly to the test. According to the results, it turned out that the system still unloads the service, and from the logs we saw how significant time gaps occurred when requesting data in the background from our server. Therefore, we started the implementation of the second option - AlarmManager.

AlarmManager is a class that provides work with, roughly speaking, an “alarm clock”. It allows you to specify the time at which the system will send a broadcast notification that will awaken our application and enable it to perform the necessary actions. There are some limitations in the operation of this method, and they need to be processed:


The first rake we stepped on was the method

 setRepeating() 

which allows you to set an “alarm” that repeats with a certain interval. Screwing this method, they began to test, and the tests showed the opposite - the “alarm clock” was not repeated. Began to understand what was happening, looked at the documentation. And it was there that found the answer to the question - starting with the 19 API lvl (Kitkat), absolutely all the “alarms” in the system became one-time. Conclusion - always read the documentation.

These rakes were not a reason for frustration, because the solution to the problem is quite simple - to start a one-time “alarm clock” and reset it after triggering. When implementing this approach, we stumbled upon the following rake - it turned out that for different API levels it was necessary to set alarm clocks in different ways, while the documentation did not say anything. But this problem was solved quite simply - by “spear” and “googling”. Below is a sample code that allows you to properly set the "alarm clocks":

 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 draw attention to the AlarmManager.RTC_WAKEUP flag - it is with the help of it that the system will allow our application to “wake up” with the screen inactive when the device is in a locked state.

This approach with “alarms” gave us the desired result - the application in the background correctly polls the server for new data. Now we are finalizing the algorithm. At the moment we are implementing and testing the following optimization, which will allow us to narrow the range of devices and thereby reduce the server load:


All good. And smaller similar crutches.

PS
In the testing process, a tool that gives the opportunity to see information on the sent guns helped a lot. This tool is available to developers for free. I recommend everyone to use it.

Pss
I predict, in the comments for sure there will be questions about the battery consumption. I conducted several tests, leaving a personal phone for the night with the included mobile Internet. The results were around 20-25% of the charge for 8-9 hours. Also, the clients to whom we sent test assemblies did not complain about problems with increasing battery consumption.

Source: https://habr.com/ru/post/274169/


All Articles