Showing posts with label BroadcastReceiver. Show all posts
Showing posts with label BroadcastReceiver. Show all posts

Sunday, 14 May 2017

Scheduling repeated tasks offline at (almost) exact times

As you probably know, starting from API level 21 (Lollipo) the best way to schedule repeated tasks on Android is using:
  • the JobScheduler API (requires API level 21+);
  • the Firebase JobDispatcher, which provides a JobScheduler-compatible API that works on all recent versions of Android (API level 9+) that have Google Play services installed.
Running apps in the background is expensive, which is especially harmful when they're not actively doing work that's important to the user.
In recognition of these issues, the Android framework team created the JobScheduler. This provides developers a simple way of specifying runtime constraints on their jobs. Available constraints include network type, charging state, and idle state.

On the other hand, the Firebase JobDispatcher uses the scheduling engine inside Google Play services(formerly the GCM Network Manager component) to provide a backwards compatible (back to Gingerbread) JobScheduler-like API.

Problem

However, both the JobScheduler API and the Firebase JobDispatcher, in order to save battery life, depend on the mainance windows of Doze.
In other words, if a user leaves a device unplugged and stationary for a period of time, with the screen off, the device enters Doze mode. In Doze mode, the system attempts to conserve battery by restricting apps' access to network and CPU-intensive services. It also prevents apps from accessing the network and defers their jobs, syncs, and standard alarms.Periodically, the system exits Doze for a brief time to let apps complete their deferred activities. During thismaintenance window, the system runs all pending syncs, jobs, and alarms, and lets apps access the network.
Doze - mantainance windows

Warning

In this post we'll focus on scheduling repeated tasks (even offline, whithout an internet connection) that should start at (almost) exact times.

AlarmManager

Although the aforementioned solution is preferable, because it enables to save battery life, there might be scenarios in which you want to schedule a repetead task to run with an exact timing, even when the device is in Doze mode.
A possible solution is using the AlarmManager. The exact methods and the pattern to follow varies depending on the Android version, because:
  • Doze was first introduced with Marshmallow (API level 23);
  • the AlarmManager methods for scheduling tasks have slightly changed with API leve 19.

The following table shows the methods to use:



repeated
exact times
display ON
repeated
exact times
display OFF
up to API 18
setRepeating
RTC or ELAPSED_REALTIME
(1)
setRepeating
RTC_WAKEUP or
ELAPSED_REALTIME_WAKEUP
API 19-22
setExact with manual re-scheduling
RTC
ELAPSED_REALTIME
(1) (2)
setExact with manual re-scheduling
RTC_WAKEUP
ELAPSED_REALTIME_WAKEUP
API 23+
setExact or setExactAndAllowWhileIdle with manual re-scheduling
(3)
setExactAndAllowWhileIdle with manual re-scheduling
(3)

(1) If an alarm is delayed (by system sleep, for example, for non _WAKEUP alarm types), a skipped repeat will be delivered as soon as possible. After that, future alarms will be delivered according to the original schedule; they do not drift over time. For example, if you have set a recurring alarm for the top of every hour but the phone was asleep from 7:45 until 8:45, an alarm will be sent as soon as the phone awakens, then the next alarm will be sent at 9:00.
(2) as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time. Legacy applications whose targetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact. With setExact the alarm will be delivered as nearly as possible to the requested trigger time.
(3) If you don't need exact scheduling of the alarm but still need to execute while idle, consider using setAndAllowWhileIdle(int, long, PendingIntent).

Frequency

For events less than 60 seconds apart, alarms aren’t the best choice: use the much more efficient Handler (http://goo.gl/CE9vAw) for frequent work.

setExactAndAllowWhileIdle: exact or not?

The method setExactAndAllowWhileIdle, used to trigger alarms even when the device is idle, can significantly impact the power use of the device when idle (and thus cause significant battery blame to the app scheduling them), so it should be used with care.

To reduce abuse, there are restrictions on how frequently these alarms will go off for a particular application:
  • Under normal system operation, it will not dispatch these alarms more than about every minute (at which point every such pending alarm is dispatched)
  • When in low-power idle modes this duration may be significantly longer, such as 15 minutes


Manual rescheduling

As you can see from the above table, starting with API 19 you have to use setExact or setExactAndAllowWhileIdle the schedule a repeated task at exact times.
Infact, as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time as described below. Legacy applications whose targetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact.

Create an AlarmManager instance:
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

Create an Intent to broadcast to a BroadcastReceiver:
Intent intent = new Intent(MyActivity.this, MyReceiver.class);

Create a PendingIntent that holds the intent:
PendingIntent pendingIntent = PendingIntent.getBroadcast(MyActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

Create the alarm:
alarmManager.setExact(AlarmManager.RTC_WAKEUP, initialDelay, pendingIntent);

Reschedule the next alarm in the onReceive method of your BroadcastReceiver using the same code described above.

WakefulBroadcastReceiver

The Alarm Manager holds a CPU wake lock as long as the alarm receiver's onReceive() method is executing. This guarantees that the phone will not sleep until you have finished handling the broadcast. Once onReceive() returns, the Alarm Manager releases this wake lock. This means that the phone will in some cases sleep as soon as your onReceive() method completes. If your alarm receiver called Context.startService(), it is possible that the phone will sleep before the requested service is launched. To prevent this, your BroadcastReceiver and Service will need to implement a separate wake lock policy to ensure that the phone continues running until the service becomes available.

An easy way to achieve this is by using a WakefulBroadcastReceiver. WakefulBroadcastReceiver is a BroadcastReceiver that receives a device wakeup event and then passes the work off to a Service, while ensuring that the device does not go back to sleep during the transition. This class takes care of creating and managing a partial wake lock for you; you must request the WAKE_LOCK permission to use it.

public class MyWakefulBroadcastReceiver extends WakefulBroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // Reschedule the alarm. See the code above.

        // Create an Intent to start a Service.
        Intent service = new Intent(context, MyService.class);

        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, service);
    }
}

The service (in this example, an IntentService) does some work. When it is finished, it releases the wake lock by calling completeWakefulIntent(intent). The intent it passes as a parameter is the same intent that the WakefulBroadcastReceiver originally passed in.

public class MyService extends IntentService {
    public MyService() {
        super("MyService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // Do some work here.
        // At this point SimpleWakefulReceiver is still holding a wake lock
        // for us.  
        // Note that when using this approach you should be aware that if your
        // service gets killed and restarted while in the middle of such work
        // (so the Intent gets re-delivered to perform the work again), it will
        // at that point no longer be holding a wake lock since we are depending
        // on SimpleWakefulReceiver to that for us.  If this is a concern, you can
        // acquire a separate wake lock here.

        // Release the wake lock after executing the task.
        SimpleWakefulReceiver.completeWakefulIntent(intent);
    }
}

Saturday, 6 May 2017

Sticky Broadcast


Sticky Broadcast(s) are a special kind of Broadcast(s) that remain active for awhile after being sent. You can send a sticky Broadcast with the method:
Context.sendStickyBroadcast()

If you register a BroadcastReceiver that listens to sticky Broadcast(s), it can receive two kind of Broadcast(s):
  1. Broadcast(s) that have just been sent
  2. Broadcast(s) that have been sent previously and are still active
You can find out what kind of Broadcast you have just received with the method:
isInitialStickyBroadcast()
returning false if the Broadcast represents an event that has just occured.

Sticky Broadcast(s) are generally used for events lasting for a certain period of time. Examples of such Broadcast(s) sent by the system are:
  • Intent.ACTION_BATTERY_CHANGED: indicates changes in the battery level. BroadcastReceiver(s) must be programmatically registered with Context.registerReceiver()
  • Intent.ACTION_DOCK_EVENT: indicates when a device is placed on a dock
  • Intent.ACTION_DEVICE_STORAGE_LOW: indicates low memory condition
public class MyBroadcastReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if(action.equals(Intent.ACTION_DOCK_EVENT)) {
            if(!isInitialStickyBroadcast()) {
                // this is a new broadcast
            } 
            else {
                // this is an old broadcast
            }
        }
    }
}

In order to send sticky Broadcast(s) you must include the permission android.permission.BROADCAST_STICKY in the Manifest file.

If you want to remove a sticky Broadcast you can use the method:
Context.removeStickyBroadcast(Intent intent)
passing the Intent that was previously broadcast.

NB. Sticky Broadcast(s) consume system resources, so use them sparingly when really needed!

API level 21

Starting from API level 21 sticky Broadcast(s) are deprecated.
"Sticky broadcasts should not be used. They provide no security (anyone can access them), no protection (anyone can modify them), and many other problems. The recommended pattern is to use a non-sticky broadcast to report that something has changed, with another mechanism for apps to retrieve the current value whenever desired".

Monday, 23 June 2014

Ordered Broadcasts

There are two types of broadcasts in Android: normal and ordered. The following table shows the main differences:


Normal
Ordered
Way of delivery
Normal broadcasts are delivered to the available receivers asynchronously, in an unspecified order
Ordered broadcasts are delivered to the available receivers one at a time, in a specified order (the order depends on the android:priority attribute of the different receivers in the AndroidManifest file)
Feedback
With normal broadcasts no feedback can be sent to the broadcaster
With ordered broadcasts the receiver can send information to the broadcaster (using the methods abortBroadcast(), seResultCode(), setResultData()


Tip: in a receiver (handling ordered broadcasts) you can call the method abortBroadcast() to make sure that the broadcast is not sent to other receivers. If you combine this method with a high value for the android:priority attribute, you can make sure that your receiver is the only one to handle that broadcast (SMS apps can use this technique).

BroadcastReceiver in Android

BroadcastReceiver

Let’s see how a BroadcastReceiver can handle ordered broadcasts (se the comments in the code for more information):
public class MyReceiver extends BroadcastReceiver {
    [...]
    public void onReceive(Context context, Intent intent) {
        //is this an ordered broadcast or not?
        if(isOrderedBroadcast() {
            //here you can handle the broadcast    
            [...]

            //here you can send information back to the broadcaster
            setResultCode(Activity.RESULT_OK);
            setResultData(“here you can pass a String”);

            //you can also create complex data to send to the broadcaster
            Bundle myBundle = getResultExtras(true);
            myBundle.put [...] //code omitted for brevity
        }
    }
}


Broadcaster

Sending an ordered broadcast is quite simple. Let’s look at the code:
Intent int = new Intent(YOUR_FILTER);
sendOrderedBroadcast(int, responseReceiver, null, RESULT_OK, null, null);
As you can see you just have to create an Intent with the filter registered for the BroadcastReceiver MyReceiver and call the method sendOrderedBroadcast. This method takes a BroadcastReceiver (responseReceiver) as a parameter: this is the receiver that will receive the information sent back by the BroadcastReceiver MyReceiver.
So you finally have to register the BroadcastReceiver responseReceiver (to handle the results) in the following way:
BroadcastReceiver responseReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        //here you can retrieve the results sent by MyReceiver
        String str = getResultData();

        //here you can retrieve the complex data sent by MyReceiver
        Bundle myBundle = getResultExtras(false);
        if(myBundle != null) {
            //here you can retrieve the data using myBundle.get... methods
        }
    }
}

Sunday, 2 February 2014

Autostart an app at bootup

Generally speaking it's not possible in Android to make your app to start automatically at bootup. But you can use a trick to achieve the same result.


Basically you have to register a BroadcastReceiver in your AndroidManifest.xml file to listen to the Intent.ACTION_BOOT_COMPLETED event. In your BroadcastReceiver class you can now start your app or Service.
This method is often used to start a Service to perform certain operations in the background (for example, to register an alarm clock).

Let's look at the code of the AndroidManifest.xml file:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
...
<receiver android:enabled="true" android:name=".BootUpReceiver"
        android:permission="android.permission.RECEIVE_BOOT_COMPLETED">

        <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
</receiver>

Now, let's suppose that you want to start a Service at bootup. The code of our BroadcastReceiver is the following:

public class BootUpReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
                Intent i = new Intent(INTENT_ACTION);  
                i.putExtra...
                [...]
                context.startService(i);  
        }

}

Of course you have to use this code only when really needed: an app starting app every time the device is booting could possibly waste system's resources.

Wednesday, 29 January 2014

Sending broadcasts within your app with a local BroadcastReceiver

In this tutorial I will explain how to send broadcasts within your own app using the LocalBroadcastManager.

To begin with, let’s start with a FAQ.


What is a BroadcastReceiver?
An Android app is made of components, including Application, Activity, Service, ContentProvider and BroacastReceiver, each of which used for a particular purpose.
So a BroadcastReceiver is a component (optional) of an Android app.

What are BroadcastReceivers generally used for?
BroadcastReceiver is a component running in the background and listening to “broacasts” sent by the Android system or by an Android app.
For example, when the user turns on or off the screen, when the level of the battery changes, when wifi is connected/disconnected, etc., the Android system sends a “broacast” with the relevant information about the event that has occured. You can set up a BroadcastReceiver that intercepts this event and performs a certain action in response (for example, starting a Service).

In your own app you can also send broacasts containing information and set up a BroacastReceiver that listens to these broadcasts and performs certain actions in response. We already saw an example in a previous tutorial about the IntentService class, with which you can use a Service to perform slow or long running operations in a background thread.
When the IntentService finishes its task you may want to communicate the result back to the calling Activity: to do this you have to send a broadcast within your IntentService containing the relevant information, and set up a BroadcastReceiver within you Activity to listen to the result.

So, what is a local BroadcastReceiver?
BroadcastReceiver is generally used to send/receive broacasts between different processes (for example, different apps) because it implements an inter process communication system.
But if the “broadcaster” and the BroadcastReceiver belong to the same process (for example, sending broacasts within your own app), a local BroadcastReceiver, which is managed by the LocalBroadcastManager class, is much more efficient.
In addition to that a local BroadcastReceiver is more secure because all relevant information stays within your own app and is not visible outside.
So a local BroadcastReceiver is a more efficient and secure BroacastReceiver that works only within your own app.


Now let’s see how to implement a local BroadcastReceiver within your Activity (here you can download the code in a more readable format):

public class YourClass extends Activity {
//the main code of the Activity is omitted for brevity

// used for filtering broadcasts (the receiver listens only to Intents with “myBroacast” set as action in the constructor)
public static final String MY_ACTION = “myBroacast”;
@Override
protected void onResume() {
super.onResume(); //call the superclass first

LocalBroadcastManager myLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
IntentFilter myIntentFilter = new IntentFilter(MY_ACTION);
//set up the local LocalBroadcastReceiver
myLocalBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//handle the broadcast here
}
};
//register the local BroadcastReceiver with the specified intent filter
myLocalBroadcastManager.registerReceiver(myLocalBroadcastReceiver, myIntentFilter);
}

@Override
protected void onPause() {
super.onPause();
LocalBroadcastManager myLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
//always unregister the receiver when it’s no longer needed to save resources
myLocalBroadcastManager.unregisterReceiver(myLocalBroadcastReceiver);
}

//instance variables
private BroadcastReceiver myLocalBroadcastReceiver;
}


When you want to send a local broadcast, you just have to use the following code:

//create an Intent with the action “myBroadcast” (the filter used in our local receiver)
Intent localBroadcastIntent = new Intent(“myBroacast”);
//you can add additional info to the Intent
localBroadcastIntent.putExtra(“risult”, risult);

LocalBroadcastManager myLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
myLocalBroadcastManager.sendBroacast(localBroadcastIntent);

Using directed broadcasts

Another way of sending broacasts within you own app is by using directed broadcasts, explicitly specifying the BroadcastReceiver that has to intercept them.
You just have to set the ComponentName in the Intent:

Intent myIntent = new Intent(action);
myIntent.setComponent(new ComponentName(packageName, className));
sendBroadcast(myIntent);

action = String representing the filter used by our BroadcastReceiver
packageName = String representing the name of the package of our BroadcastReceiver (like “com.androidthetechnicalblog.example”)
className = String representing the name of the class of our BroadcastReceiver

This way our broadcast is received only by the specified BroadcastReceiver.
Directed broadcasts can also be used with different processes/applications.


If you like this blog please share it on Facebook, Twitter or Google+ using the icons or links below. 
You can also subscribe using the form on the right to be notified via mail or feed rss when new tutorials are posted.