Wednesday, 23 October 2013

Android BroadcastReceiver Tutorial

BroadcastReceiver
BroadcastReceiver are used to register for System and application events. Android run time system will notify these components once the event for which they are register happens.Upon notification the broadcast receiver will take appropriate action.


A broadcast receiver is a component that responds to system-wide broadcast announcements. Many broadcasts originate from the system—for example, a broadcast announcing that the screen has turned off, the battery is low, or a picture was captured. Applications can also initiate broadcasts—for example, to let other applications know that some data has been downloaded to the device and is available for them to use. Although broadcast receivers don't display a user interface, they may create a status bar notification to alert the user when a broadcast event occurs. More commonly, though, a broadcast receiver is just a "gateway" to other components and is intended to do a very minimal amount of work. For instance, it might initiate a service to perform some work based on the event. Other examples of Broadcast Receiver from applications are new friend notifications, new friend feeds, new message etc. on your facebook application.



Notifications like incoming messages, wi-fi, and Bluetooth activation signal, new wi-fi in the range, low battery signal are the real time examples of the BroadcastReceiver. Thus BroadcastReceiver are everywhere in the android system.




Example of the system broadcast Intent are:

 Block Diagram of the system:



Registering BroadcastReceiver:

There are two ways to register broadcast receiver:

  •   Static: Use <receiver> tag in your Manifest files (AndroidManifest.xml).
  •   Dynamic: Use Context.registerReceiver () method to register the receiver dynamically.
Implementation: 

 Create your own class extending BroadcastReceiver android class. If the registered broadcast event happens then android system will notify the Receiver class. Android system calls onReceive () method of the BroadcastReceiver. 

There are two major classes of broadcasts that can be received:


  • Normal broadcasts:(sent with Context.sendBroadcast) are completely asynchronous. All receivers of the broadcast are run in an undefined order, often at the same time. This is more efficient, but means that receivers cannot use the result or abort APIs included here.

  • Ordered Broadcast:(Send with                                    context.sendOrderedBroadcast()) These broadcast are delivered to one receiver at a time,As each receiver executes in turn,It can propagate result to the next receiver or it can completely abort the broadcast so that it wont be passed to other receivers.The order receivers run in can be controlled with the android:priority attribute of the matching intent filter;receivers with the same priority will be run in an arbitrary order
Even in the case of normal broadcast,the system may in some situations revert to delivering the broadcast one receiver at a time. 
Security: 
Receivers used with Context APIs are by their nature a cross-application facility,so you must consider how other applications may be able to abuse your use of them.Some things to consider are 
  • The Intent namespace is global. Make sure that Intent action names and other strings are written in a namespace you own, or else you may inadvertently conflict with other applications.
  • When you use registerReceiver(BroadcastReceiver, IntentFilter), any application may send broadcasts to that registered receiver. You can control who can send broadcasts to it through permissions described below.
  • When you publish a receiver in your application's manifest and specify intent-filters for it, any other application can send broadcasts to it regardless of the filters you specify. To prevent others from sending to it, make it unavailable to them with  android:exported="false".
  • When you use sendBroadcast(Intent) or related methods, normally any other application can receive these broadcasts. You can control who can receive such broadcasts through permissions described below. Alternatively, starting with ICE_CREAM_SANDWICH, you can also safely restrict the broadcast to a single application with Intent.setPackage( ).
None of these issues exist when using LocalBroadcastManager, since intents broadcast it never go outside of the current process.
Access permissions can be enforced by either the sender or receiver of a broadcast.
To enforce a permission when sending, you supply a non-null permission argument to sendBroadcast(Intent, String) or sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle). Only receivers who have been granted this permission (by requesting it with the <uses-permission> tag in their AndroidManifest.xml) will be able to receive the broadcast.
To enforce a permission when receiving, you supply a non-null permission when registering your receiver -- either when callingregisterReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler) or in the static <receiver> tag in your AndroidManifest.xml. Only broadcasters who have been granted this permission (by requesting it with the <uses-permission> tag in their AndroidManifest.xml) will be able to send an Intent to the receiver.
From API level 11(3.0)  android receiver will not receive any broadcast intent till application has run at least once .Application must not be stopped explicitly from android settings. 
Receiver Lifecycle :
 
A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent). Once your code returns from this function, the system considers the object to be finished and no longer active.
This has important repercussions to what you can do in an onReceive(Context, Intent) implementation: anything that requires asynchronous operation is not available, because you will need to return from the function to handle the asynchronous operation, but at that point the BroadcastReceiver is no longer active and thus the system is free to kill its process before the asynchronous operation completes.
In particular, you may not show a dialog or bind to a service from within a BroadcastReceiver. For the former, you should instead use the NotificationManager API. For the latter, you can use Context.startService() to send a command to the service.
From API level 11(Android 3.0) goAsynch()  method is there to help. This method returns object of type PendingResult. Receiver will be alive till we call PendingResult.finish() .
 
Example 1: 
Bellow is the simple example with event Intent.ACTION_BATTERY_LOW and displays dialog with message to the user.
AndroidManifest.xml will look like this:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.swapnil.batterytest"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.swapnil.batterytest.StartActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="com.swapnil.batterytest.MyBatteryLevelReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.ACTION_BATTERY_LOW"/>
            </intent-filter>
        </receiver>
    </application>
</manifest>                                                                        
BroadcastReceiver will look like this:



public class MyBatteryLevelReceiver extends BroadcastReceiver

{

                @Override

                public void onReceive(Context context, Intent intent)

                {

                                if(intent.getAction().equals(Intent.ACTION_BATTERY_LOW))

                    {         

                             Toast.makeText(context,R.string.battery_low_message,Toast.LENGTH_LONG).show();

                             // do your task here.
                    }
                }

}                                                                                                                                                                                                                        
 
If you want to check the battery level :
 

  IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
  Intent batteryStatus = context.registerReceiver(null, ifilter);
  int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
  int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
  int percent = (level*100)/scale;
  if(percent <= 10 && percent > 5)
  {
                 
  }                                                                                 
Registering for custom events:
Android manifest will look like this

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.swapmeen.custombroadcastreceiver"

    android:versionCode="1"

    android:versionName="1.0" >



    <uses-sdk

        android:minSdkVersion="8"

        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.swapmeen.custombroadcastreceiver.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="com.swapmeen.custombroadcastreceiver.CustomBroadcastReceiver" >
            <intent-filter>
                <action android:name="com.swapmeen.custombroadcastreceiver.custombroadcast" />
            </intent-filter>
        </receiver>
    </application>

</manifest>
 
You need to register receiver with custom BroadcastReceiver with 
custom event which has unique action.
Following code is used to send the Intent to registered 
BroadcastReceiver:

 Intent intent = new Intent();
 intent.setAction("com.swapmeen.custombroadcastreceiver.custombroadcast");
 sendBroadcast(intent);                                                                       
 Above code can’t be used with system events. Above method is
 asynchronous and returns immediately without waiting for
 onReceive () to finish ().


Registering BroadcastReceiver Dynamically: 


Following are the methods to register BroadcastReceiver:


Publicabstract Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter)


     Public abstract Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handlerscheduler)  

You must unregister receiver before exiting the activity otherwise 

you will get leaked broadcast receiver error.



LocalBroadcastManager:  


Helper to register for and send broadcasts of Intents to local objects

within your process. This is has a number of advantages over

 sending global broadcasts with sendBroadcast(Intent): 


  • You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data. 

  • It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit.

  • It is more efficient than sending a global broadcast through the system.

  • This is faster and secure method. 
Example of the LocalBroacastManager:

public class StartActivity extends Activity {



       @Override

       protected void onCreate(Bundle savedInstanceState) {

              super.onCreate(savedInstanceState);

              setContentView(R.layout.activity_start);

       }

       @Override
       protected void onResume()
       {
              super.onResume();
              LocalBroadcastManager.getInstance(this).registerReceiver(custom_receiver,new IntentFilter("custom_data"));
       }
      
       private BroadcastReceiver custom_receiver = new BroadcastReceiver()
       {
                @Override
                public void onReceive(Context context, Intent intent)
                {
                  // Extract data included in the Intent
                  String message = intent.getStringExtra("data");
                  Log.d("TAG", "message received: " + message);
                }
              };
       @Override
       protected void onPause()
       {
              LocalBroadcastManager.getInstance(this).unregisterReceiver(custom_receiver);
              super.onPause();
       }
       @Override
       public boolean onCreateOptionsMenu(Menu menu) {
              // Inflate the menu; this adds items to the action bar if it is present.
              getMenuInflater().inflate(R.menu.start, menu);
              return true;
       }
      
       int i=0;
      
       public void sendbroadcast(View view)
       {
                Intent intent = new Intent("custom_data");
                i++;
                intent.putExtra("data", "Button clicked i: "+i);
                LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
       }

}
 
 
Using PackageManager with statically registered Receiver:

You can use the PackageManager to enable/disable a BroadcastReceiver in declared in the Manifest. The Broadcast Receiver will get fired only when it is enabled.
Use this to create a Component.


ComponentName component = new ComponentName(context, MyReceiver.class);
 
Check if the Component is enabled or disabled
 

int status = context.getPackageManager().getComponentEnabledSetting(component);

if(status == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {

    Log.d("receiver is enabled");

} else if(status == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {

    Log.d("receiver is disabled");

}
 
Enable/Disable the component(Broadcast Receiver in your case):

//Disable

context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED , PackageManager.DONT_KILL_APP);

//Enable          

context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED , PackageManager.DONT_KILL_APP);
  
Sticky Broadcast Intent:

These are broadcasts whose data is held by the system after being finished, so that clients can quickly retrieve that data without having to wait for the next broadcast.

Permission required:

<uses-permission android:name="android.permission.BROADCAST_STICKY"/> - Allows an application to broadcast sticky intents.



Example:

Intent intent = new Intent("some.custom.action");

intent.putExtra("some_boolean", true);

sendStickyBroadcast(intent);
 
Perform a sendBroadcast(Intent) that is "sticky," meaning the Intent you are sending stays around after the broadcast is complete, so that others can quickly retrieve that data through the return value of registerReceiver(BroadcastReceiver, IntentFilter). In all other ways, this behaves the same as sendBroadcast(Intent).
 

// Register for the battery changed event
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
 
/ Intent is sticky so using null as receiver works fine
// return value contains the status
Intent batteryStatus = this.registerReceiver(null, filter);
 
// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING
  || status == BatteryManager.BATTERY_STATUS_FULL;
 
boolean isFull = status == BatteryManager.BATTERY_STATUS_FULL;
 
// How are we charging?
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;

boolean acCharge = chargePlug == BatteryManager.BATTERY  

1 comment:

  1. Informative post indeed, I’ve being in and out reading posts regularly and I see a lot of engaging people sharing things and majority of the shared information is very valuable and so, here’s my fine read.
    Android Training Institute in Chennai | Android Training Institute in anna nagar | Android Training Institute in omr | Android Training Institute in porur | Android Training Institute in tambaram | Android Training Institute in velachery




    ReplyDelete