Thursday, 6 March 2014

Understanding BROADCAST RECEIVERS in Android : PART 2

We have discussed about broadcasts by Android system in the previous post PART1 and we are going to discuss about Broadcasts by the user in this post.

User also can send broadcasts whenever he wants. For ex: On click of a button OR on successful download of a file. So the receivers who are intended to receive this announcement can receive the message and react. Registering, Implementing and unregistering the receiver for broadcasts by the user are same as we discussed previously in PART1.  The only difference is user has to send the announcement of event explicitly. sendBroadcast() method is used to send the broadcasts explicitly.
We should use same action name while registering the receiver and sending the broadcast as below.

Registering statically:
<receiver android:name=".MyReciever" >
    <intent-filter>
      <action android:name="com.smartmobsolutions.custom_broadcast_action"/>
    </intent-filter>
</receiver>
Or you can register dynamically as below:
IntentFilter iff = new IntentFilter("com.smartmobsolutions.custom_broadcast_action");
registerReceiver(yourReceiverClassObject, iff);

On click of a button, you can send broadcasts in 2 simple steps:
1.      Create an intent by passing the action name to the constructor of Intent class. Or you can also create an empty intent and set the action by using setAction() of Intent class.  Optionally you can send some data to the receivers using putExtra() methods. You can get back this intent (which is being used to broadcast) as one of parameters of onReceive() method of your receiver class.
2.      Pass intent to sendBroadcast() method.

Intent intent = new Intent();
intent.setAction("com.smartmobsolutions.custom_broadcast_action");
intent.putExtra(“key”, your_data);
sendBroadcast(intent);

So, now we’ve sent a broadcast explicitly. All the receivers (means applications) who are registered for the same event will get triggered now.
public class MyReciever extends BroadcastReceiver{

            @Override
            public void onReceive(Context context, Intent intent) {
                        String data = intent.getStringExtra("key");
                              //The task to do when the required event happens.

            }
}
In this way also you can share the data across the applications.  You can set the priority if you want the receiver to get triggered first among all others as discussed in PART1.  
NOTE 1: As of Android 3.1 the Android system excludes all receivers from receiving intents by default if the corresponding application has never been started by the user or if the user explicitly stopped the application via the Android menu (in Manage → Application). This is an additional security feature as the user can be sure that only the applications he started will receive broadcast intents. This does not mean the user has to start the application again after a reboot. The Android system remembers that the user already started it. Only one start is required without a forced stop by the user.
NOTE 2: All the receivers are triggered in asynchronous manner. In other words, sendBroadcast() will send the broadcast and return immediately i.e. It doesn’t wait until all the receivers who are registered for matching event have triggered and executed.
(NOTE1 Attribution: Vogella)

Ordered Broadcasts: There might be a situation where you need to receive the result from the receiver to the calling component (i.e. most probably an activity). A normal broadcast will be received by receivers asynchronously and reacts. But an OrderedBroadcast receives the event, reacts and returns the result to calling component. sendOrderedBroadcast() method is used to send Ordered Broadcasts.  One of the arguments of sendOrderedBroacast() is result receiver or final receiver which is used to get the result in the calling component.

Main.xml :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
     tools:context=".MainActivity" >

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="sendOrederedEvent"
        android:text="Send Ordered Broadcast"
        android:textColor="@android:color/black"
        android:textSize="17sp"
        android:textStyle="bold" />
</RelativeLayout>

MainActivity.java:
public class MainActivity extends Activity {

private static final String EVENT_ACTION_NAME  = "com.smartmobsolutions.android.ordered_broadcastreceivers_demo_app";
           
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void sendOrederedEvent(View v){
 Intent i = new Intent(EVENT_ACTION_NAME);
 sendOrderedBroadcast(i, null, finalResultReceiver, null, Activity.RESULT_OK, null, null);
}
           
private BroadcastReceiver finalResultReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent){
            Toast.makeText(context, "Final Result Receiver",Toast.LENGTH_SHORT).show();
            Bundle results = getResultExtras(true);
            Toast.makeText(context, "Result = "+results.getString("result2"), Toast.LENGTH_SHORT).show();
                            
}
};
           
}


Register a receiver with priority. Give max priority to get it triggered first.

<receiver android:name=".Receiver1">
   <intent-filter android:priority="2">
   <action android:name="com.smartmobsolutions.android.ordered_broadcastreceivers_demo_app"/>
   </intent-filter>
</receiver>

And create below Receiver1.java in same app:

public class Receiver1 extends BroadcastReceiver{

            @Override
            public void onReceive(Context context, Intent intent) {
                        Toast.makeText(context, "Receiver1 triggered", Toast.LENGTH_SHORT).show();
                        Bundle results = getResultExtras(true);
                        results.putString("result1", "result in receiver1");

            }

}

Create another application with the Receiver of same event. Below is the Receiver class and registration of receiver class with low priority in manifest

public class Receiver2 extends BroadcastReceiver{

            @Override
            public void onReceive(Context context, Intent intent) {
                         Toast.makeText(context, "Receiver2 triggered", Toast.LENGTH_SHORT).show();
                         Bundle results = getResultExtras(true);
                         String resultInReceiver1 = results.getString("result1");
                 results.putString("result2", resultInReceiver1+", result in receiver2");
             }

}
<receiver android:name=".Receiver2">
   <intent-filter android:priority="1">
   <action android:name="com.smartmobsolutions.android.ordered_broadcastreceivers_demo_app"/>
    </intent-filter>
</receiver>

So, now we have two different applications with different receivers i.e Receiver1 with high priority in App1 and Receiver2 with low priority in App2. Both are intended to receive same action event. So When you click the button in App1, sendOrderedBroadcast() method will send an ordered broadcast to the receivers. Receiver1 will get triggered first and pass the result to its next receiver(s). Now the next receiver with max priority (i.e Receiver2) will get triggered and receive the result from previous receiver and pass the result to its next receivers if any with low priority, otherwise final receiver which is passed as an argument to sendOrderedBroadcast() method will get triggered.

We can even stop the broadcasting of events using abortBroadcast() method. i.e if we use abortBroadcast() method in Receiver1 class (which has max priority) then all other receivers who are registered for the same action (with lower priority) will not get triggered so the result will be passed directly from Receiver1 to final receiver in MainActivity.

This is how we can broadcast the events/intents in an ordered manner and pass the result back to calling component (i.e MainActivity in this example) .



1 comment:

  1. Hello....Its looking good and very helpfull..

    Thanks...

    ReplyDelete