Thứ Bảy, 16 tháng 3, 2013

Broadcast Receivers


Broadcast Receivers
broadcast receiver is a component of an Android process, along with activities, content providers, and services 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. As the name indicates, a broadcast receiver responds to a broadcast message sent by a client. The message itself is an Android broadcast intent. A broadcast intent (message) can invoke (or be responded to by) more than one receiver. A component such as an activity or a service uses the sendBroadcast() method available on the Context class to send a broadcast event. The argument to this method

A broadcast receiver is implemented as a subclass of BroadcastReceiver and each broadcast is delivered as an Intent object. These receiving components (broadcast receivers) then need to be registered in the manifest file through a receiver tag to indicate that the class is interested in responding to a certain type of broadcast intent.
. is an intent.
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.

Process Lifecycle

A process that is currently executing a BroadcastReceiver (that is, currently running the code in its onReceive(Context, Intent) method) is considered to be a foreground process and will be kept running by the system except under cases of extreme memory pressure. 
Once you return from onReceive(), the BroadcastReceiver is no longer active, and its hosting process is only as important as any other application components that are running in it. This is especially important because if that process was only hosting the BroadcastReceiver (a common case for applications that the user has never or not recently interacted with), then upon returning from onReceive() the system will consider its process to be empty and aggressively kill it so that resources are available for other more important processes.
A Broadcast Receiver must return from its onReceive  handler within 10 seconds. as a part of Android responsiveness model.This means that for longer-running operations you will often use a Service in conjunction with a BroadcastReceiver to keep the containing process active for the entire time of your operation.

Broadcast receivers can be registered in two ways
1.    It can be registered in Android Manifest file
2.   Inside an activity registered at runtime dynamically via theContext.registerReceiver()method.  The First example SRM_BroadcastRecevierTutorial demonstrates  a standalone broadcast receiver application which gets invoked on system message on wallpaper being changed .

Exercise 1 Understanding Broadcast Receiver registered through manifest file


  1. Create a Android project SRM_BroadcastReceiverTutorial and unselect createActivity checkbox
Figure 1 Creating the Project

2.Make a WallPaperNotificationReceiver.java file which extends BroadcastReceiver
 package in.ac.srmuniv;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;

import android.util.Log;
import android.widget.Toast;

public class WallPaperNotificationReceiver extends BroadcastReceiver {

                @Override
                public void onReceive(Context context, Intent intent) {
                                this.sendNotification(context, "You have changed Wallpaper");
    }
    private void sendNotification(Context ctx, String message)
    {
                //Get the notification manager
                String ns = Context.NOTIFICATION_SERVICE;
                NotificationManager nm =
                                (NotificationManager)ctx.getSystemService(ns);
               
                //Create Notification Object
                                int icon = R.drawable.ic_launcher;
                                CharSequence tickerText = "Hello";
                                long when = System.currentTimeMillis();
                               
                                Notification notification =
                                                new Notification(icon, tickerText, when);

                                //Set ContentView using setLatestEvenInfo
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setData(Uri.parse("http://www.google.com"));
                    PendingIntent pi = PendingIntent.getActivity(ctx, 0, intent, 0);
                    notification.setLatestEventInfo(ctx, "Intimation", message, pi);
                                               
                    //Send notification
                                nm.notify(1, notification);
                                Toast.makeText(ctx,"Hello Nawin",Toast.LENGTH_LONG).show();
    }

}

3.Make the entries in manifest file to register Broadcast receiver to specific intent message(s) to which it should respond
xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="in.ac.srmuniv"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
       
    <receiver android:name=".WallPaperNotificationReceiver">
           <intent-filter>
            <action android:name="android.intent.action.WALLPAPER_CHANGED" />
            </intent-filter>
    </receiver>
 </application>
</manifest>

4.Run the Application after it has successfully installed in the emulator click menu button to select and wallpaper settings to change the wallpaper
 Figure 2 Selecting wall Paper
 5. Choose from wallpapers  and set one as wallpaper

 Figure 3 Setting Wallpaper
6.On selecting a wallpaper you can see a Toast message appearing and a notification appears at Notification bar

Figure 4    Notification Displayed                 
 
7. Expanding the Notification and selecting the notification message will call the browser

                                                 Figure 5 Notification Expanded 
How it Works
                             The   WallPaperNotificationReceiver is a BroadcastReceiver  which overrides its base class method onReceive. System service sends broadcast with Intent message android.intent.action.WALLPAPER_CHANGED  when wallpaper is changed by the user .Our application which has registered for the Intent message in the manifest file for our WallPaperNotificationReceiver will get invoked and onReceive(Context context, Intent intent) method will be called .In the onReceive methodsendNotification local method is called which generates Notification in notification bar.When user expands the notification and interacts parceled intent in pending intent opens web browser through intent filtering action  which has Intent.ACTION_VIEW as action the data as  URL http://www.srmuniv.ac.in
Android generates  many in build messages by its System process which can be registered to be received for various actions to be performed in an application.A few listed below are
android.intent.action.BATTERY_LOW
android.intent.action.DATA_SMSRECEIVED
android.intent.action.BOOT_COMPLETED
android.intent.action.CAMERA_BUTTON
For example  the Intent action ACTION_BATTERY_LOW broadcasts awarning when the battery is low.If your application is battery –cunsuming service of some kid you might want to listen for the Broadcast and shutdown your service until battery power is sufficient.

Pending Intent:
            The PendingIntent can be handed to other applications so that they can perform the action you described on your behalf at a later time. By giving a PendingIntent to another application, you are granting it the right to perform the operation you have specified as if the other application was yourself (with the same permissions and identity).

String ns = Context.NOTIFICATION_SERVICE;
                NotificationManager nm =
                                (NotificationManager)ctx.getSystemService(ns);
                        ---------
Notification notification =
                                                new Notification(icon, tickerText, when);

                                //Set ContentView using setLatestEvenInfo
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setData(Uri.parse("http://www.srmuniv.ac.in"));
                    PendingIntent pi = PendingIntent.getActivity(ctx, 0, intent, 0);
                    notification.setLatestEventInfo(ctx, "Intimation", message, pi);
                    //Send notification
                                nm.notify(1, notification);
Sets the contentView field to be a view with the standard "Latest Event" layout. "pi" is an intent to launch when the user clicks the expanded notification

                The Second example SRM_BroadcastRecevierTutorial2 demonstrates  a dynamically registered broadcast receiver in an activity . In this example a  custom generated Intent message  which gets  broadcasted through a long running service on an invent  invokes a broadcast receiver which being a inner class declared within the activity. 

Exercise 2 Understanding Broadcast Receiver registered dynamically within an Activity

1.Create a Android project SRM_BroadcastReceiverTutorial2 and  add an Activity to it named ClockTimeactivity.java following is the code

 package in.ac.srmuniv;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Typeface;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class ClockTimeActivity extends Activity {
                private static final String TAG="BroadcastTime";
                private Intent in;
               
                private BroadcastReceiver timerec=new BroadcastReceiver(){

                                @Override
                                public void onReceive(Context c, Intent i) {
                                               
                                                //Getting the values from intent and updating log
                                               
                                                int h=i.getIntExtra("hrs", 0);
                                                int m=i.getIntExtra("min", 0);
                                                int s=i.getIntExtra("sec", 0);
                                                Log.d(TAG, Integer.toString(h));
                                                Log.d(TAG, Integer.toString(m));
                                                Log.d(TAG, Integer.toString(s));
                                               
                                                //Printing Time to Textview
                                               
                                                TextView th=(TextView) findViewById(R.id.hr);
                                                TextView tm=(TextView) findViewById(R.id.mn);
                                                TextView ts=(TextView) findViewById(R.id.sc);
                                                if(h>=10)
                                                                th.setText(Integer.toString(h)+":");
                                                else
                                                                th.setText("0"+Integer.toString(h)+":");
                                                if(m>=10)
                                                                tm.setText(Integer.toString(m)+":");
                                                else
                                                                tm.setText("0"+Integer.toString(m)+":");
                                                if(s>=10)
                                                                ts.setText(Integer.toString(s));
                                                else
                                                                ts.setText("0"+Integer.toString(s));
                                               
                                                }
                               
                };
               
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
       
        //Intent for starting the Time display service
       
        in=new Intent(this,BroadCastTimeService.class);
        startService(in);
       //(BroadcastTime.BROADCAST_TIME));
       
        //Code for opening alarm set activity
       
        TextView txth = (TextView) findViewById(R.id.hr);
        TextView txtm = (TextView) findViewById(R.id.mn);
        TextView txts = (TextView) findViewById(R.id.sc);
       
       
                Typeface fonth = Typeface.createFromAsset(getAssets(), "MyriadPro-Bold.otf"); 
            txth.setTypeface(fonth);
            txtm.setTypeface(fonth);
            txts.setTypeface(fonth);
    
    }

                @Override
                protected void onPause() {
                                // TODO Auto-generated method stub
                                super.onPause();
                                unregisterReceiver(timerec);
                }

                @Override
                protected void onResume() {
                                // TODO Auto-generated method stub
                                super.onResume();
                                 registerReceiver(timerecnew IntentFilter("in.ac.srmuniv.displaytime"));
                }
}

  1. Create main.xml in layout folder for UI Design of ClockTimeActivity.Code listing is given below

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
         <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:background="@drawable/clockbg"
            android:paddingLeft="20dp" >

<TextView android:layout_width="wrap_content" android:text="00:"android:id="@+id/hr" android:layout_height="wrap_content"android:textSize="16pt" android:textColor="#FFFFFF"></TextView>
<TextView android:layout_width="wrap_content" android:text="00:"android:id="@+id/mn" android:layout_height="wrap_content"android:textSize="16pt" android:textColor="#FFFFFF"></TextView>
<TextView android:layout_width="wrap_content" android:text="00"android:id="@+id/sc" android:layout_height="wrap_content"android:textSize="16pt" android:textColor="#FFFFFF"></TextView>
        </LinearLayout>
   
<TextView android:text="Digital Clock" android:layout_height="wrap_content"
android:id="@+id/textView1"
android:layout_width="wrap_content" android:layout_below="@+id/btnset"
android:layout_marginTop="10dp" android:layout_centerHorizontal="true">
</TextView>
</RelativeLayout>
  1. Add clockbg.png  image to drawable folder and MyriadPro-Bold.otf  font  file in assets folder to have appropriate font for Digital Clock display
4.Add an BroadCastTimeService.java which extends Service following is the code listing

package in.ac.srmuniv;
import java.util.Date;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

public class BroadCastTimeService extends Service {


   private static final String TAG="BroadcastTime";
   static final String BROADCAST_TIME = "in.ac.srmuniv.displaytime";
   public final Handler handler = new Handler();
   Intent i;
      @Override

   public IBinder onBind(Intent arg0) {

         // TODO Auto-generated method stub

         return null;

   }
   
   @Override

   public void onCreate()

   {
         super.onCreate();

         i = new Intent(BROADCAST_TIME);
   }
  

  @Override

   public int onStartCommand(Intent intent, int flags, int startId) {

         // TODO Auto-generated method stub

         handler.removeCallbacks(update);

         handler.postDelayed(update, 1000);

         return Service.START_NOT_STICKY;

   }

   private Runnable updatenew Runnable(){

         @Override

         public void run() {

               Log.d(TAG"Updated Time");

               i.putExtra("hrs"new Date().getHours());

               i.putExtra("min"new Date().getMinutes());

               i.putExtra("sec"new Date().getSeconds());

               sendBroadcast(i);

               handler.postDelayed(this, 1000);

         }
  

   };
5.Modify the  manifest file as per the code listing shown below which should include the entry for BroadCastTimeService but no registration is done for the Broadcast receiver since it is dynamically registered through coding



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

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

    package="in.ac.srmuniv"

    android:versionCode="1"

    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />


    <application

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name" >

        <activity

            android:name=".ClockTimeActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>


        <service android:name=".BroadCastTimeService" />

    </application>

</manifest>


6.Run the application in the emulator you can see the Digital clock viewed in theClockTimeActivity

Figure 10 Shows the screen shot of Digital clock 
How It Works
            ClockTimeActivity contains an Broadcast receiver object instantiated as anonymous inner class with onReceive  method overridden.onCreate method in
ClockTimeActivity calls the service class through the following code

in=new Intent(this,BroadCastTimeService.class);
            startService(in);

BroadCastTimeService  is a service used to generate time delay every one second through separate thread through Runnable interface the thread object update is managed by  Handler object handler.The onCreate method of BroadCastTimeService class creates a intent to be broadcast with custom action message "in.ac.srmuniv.displaytime".int onStartCommand(Intent intent, int flags, intstartId) is called after onCreate method where service is in active state .
                                    handler.removeCallbacks(update);
                        handler.postDelayed(update, 1000);
handler objects postDelayed method manages background thread of Runnableupdate object by calling it at specified interval of time in our example every 1000 seconds.
                                    i.putExtra("hrs"new Date().getHours());
                                    i.putExtra("min"new Date().getMinutes());
                                    i.putExtra("sec"new Date().getSeconds());
                                    sendBroadcast(i);
                        handler.postDelayed(this, 1000);
The above code in run method adds hours, minutes and seconds of current time from Date class as extra message to the Intent  created in onCreate method throughputExtra method and sendBroadcast method broadcasts the intent.
       ClockTimeActivity which has registered the receiver dynamically in onResume method with custom Intent action "in.ac.srmuniv.displaytime"
Receives the broadcast message. Since the  Broadcast receiver is implemented inside the Activity in onReceive method  it is able to manipulate  TextViews for  displaying hours, minuite and seconds with the values taken from the Intent message through getExtra() method.
            Now the service will be sending broadcast message managed by handler object every one second the broadcast receiver in activity will be called every one second which in turn updates Time display in the Activity through its TextViews.
                                    int h=i.getIntExtra("hrs", 0);
                                    int m=i.getIntExtra("min", 0);
                                    int s=i.getIntExtra("sec", 0);
                                   
                                    //Printing Time to Textview
                                   
                                    TextView th=(TextView) findViewById(R.id.hr);
                                    TextView tm=(TextView) findViewById(R.id.mn);
                        TextView ts=(TextView) findViewById(R.id.sc);
    

Services
            Services are long running process which does not need UI. Services are created when manually started (via an API call) or when some activity tries connecting to the service via interprocess communication (IPC). Services will live until specifically shut down or until Android is desperate for RAM and destroys them prematurely we will explore them more in detail in subsequent tutorial

Handler
               All Android application components — including Activities, Services, and Broadcast Receivers start on the main application thread. As a result,time-consuming processing in any component will block all other components including Services and the visible Activity Using background threads is vital to avoid the “Application Unresponsive” Dialog box Unresponsiveness is defi ned in Android as Activities that don’t respond to an input event (such as a key press) within 5 seconds and Broadcast Receivers that don’t complete their onReceive handlers within 10 seconds.Android offers two alternatives for backgrounding your processing. The AsyncTask class lets you define an operation to be performed in the background, then provides event handlers you can use to monitor progress and post the results on the GUI thread. Alternatively, you can implement your own Threads and  use the Handler class to synchronize with the GUI thread before updating the UI The most flexible means of making an Android-friendly background thread is to create an instance of a Handler subclass. You need only one Handler object per activity, and you do not need to manually register it. Merely creating the instance is sufficient to register it with the Android threading  subsystem. Your background thread can communicate with the Handler, which will do all of its work on the activity’s UI thread. This is important, as UI changes, such as updating widgets,should occur only on the activity’s UI thread.You have two options for communicating with the Handler: messages and Runnable objects. 





















Không có nhận xét nào:

Đăng nhận xét