BroadcastReceiver处理耗时操作

来源:互联网 发布:手机变音软件 编辑:程序博客网 时间:2024/06/05 08:40

Long-Running Receivers and Services 


So far, we have covered the happy path of broadcast receivers w here the execution of a 
broadcast receiver is unlikely to take more than ten seconds. As it turns out, the 
problem space becomes a bit complicated if we  want to perform tasks that take longer 
than ten seconds. 


To understand why, let’s quickly review a few facts about broadcast receivers:  
 A broadcast receiver, like other components of an An droid process, 
runs on the main thread.  
 Holding up the code in a broadcast receiver will hold up the main 
thread and will result in ANR.  
 The time limit on a broadcast receiver is ten seconds compared to five 
seconds for an activity. It is a touch of a reprieve, but the limit is still 
there. 
 The process hosting the broadcast receiver will start and terminate 
along with the broadcast receiver execution. Hence the process will 
not stick around after  the broadcast receiver’s onReceive()  method 
returns. Of course, this is assumi ng that the process contains only the 
broadcast receiver. If the process contains other components, such as 
activities or services, that are alr eady running, then the lifetime of the 
process takes these component life cycles into account as well. 
 Unlike a service process, a broadcast receiver process will not get restarted.  
 If a broadcast receiver were to start  a separate thread  and return to 
the main thread, Android will assume that the work is complete and 
will shut down the process even if there are threads running, bringing 
those threads to abrupt stop.  
 Android acquires a partial wake lock when invoking a broadcast 
service and releases it when it re turns from the service in the main 
thread. A wake lock is a mechanism  and an API class available in the 
SDK to keep the device from going to sleep or wake it up if it is already asleep. 


Given these predicates, how can we ex ecute longer-running code in response to a 

broadcast event?

Long-Running Broadcast Receiver Protocol 
The answer lies in resolving the following:  
We will clearly need a separate thread so that the main thread can get 
back and avoid ANR messages.  
To stop Android from killing the process and hence the worker thread, 
we need to tell Android that this process contains a component, such 
as a service, with a life cycle. So we need to create or start that 
service. The service itself cannot directly do the work for more than 
five seconds because that happens on the main thread, so the service 
needs to start a worker thread and let the main thread go.  
For the duration of the worker thread’s execution, we need to hold on 
to the partial wake lock so that the device won’t go to sleep. A partial 
wake lock will allow the device to run code without turning on the 
screen and so on, which allows for longer battery life. 
The partial wake lock must be obtained in the main line code of the 
receiver; otherwise, it will be too late. For example, you cannot do this 
in the service, because it may be too late between the  startService()
being issued by the broadcast receiver and the  onStartCommand() of a 
service that begins execution. 
Because we are creating a service, the service itself can be brought 
down and brought back up because of low-memory conditions. If this 
happens, we need to acquire the wake lock again. 
When the worker thread started by the onStartCommand() method of 
the service completes its work, it needs to tell the service to stop so 
that it can be put to bed and not brought back to life by Android.  
It is also possible that more than one broadcast event can occur. 
Given that, we need to be cautious about how many worker threads 
we need to spawn. 


Given these facts, the recommended protocol for extending the life of a broadcast 
receiver is as follows: 

1. Get a (static) partial wake lock in the onReceive()  method of the 
broadcast receiver. The partial wake lock needs to be static to allow 
communication between the broadcast receiver and the service. There 
is no other way of passing a reference of the wake lock to the service, 
as the service is invoked through a default constructor that takes no 
parameters. 
2. Start a local service so that the process won’t be killed.  
CHAPTER 19:  Broadcast Receivers and Long-Running Services  516 
3.   In the service, start a worker thread  to do the work. Do not do the work 
in the  onStart()  method of the service. If you do, you are basically 
holding up the main thread again. 
4.   When the worker thread is done, tell the service to stop itself either 
directly or through a handler.  
5.   Have the service turn off the static  wake lock. To repeat, a static wake 
lock is the only way to communicate  between a service and its invoker, 
in this case the broadcast service,  because there is no way to pass a 
wake lock reference to the service.

You can read the book for more info, the following is an example:

public abstract class ALongRunningNonStickyBroadcastService  extends IntentService  {      public static String tag = "ALongRunningBroadcastService";     protected abstract void       handleBroadcastIntent(Intent broadcastIntent);          public ALongRunningNonStickyBroadcastService(String name){           super(name);         }          /*      * This method can be invoked under two circumstances       * 1. When a broadcast receiver issues a "startService"       * 2. when android restarts it due to pending "startService" intents.       *        * In case 1, the broadcast receiver has already      * setup the "lightedgreenroom".      *        * In case 2, we need to do the same.       */      @Override      public void onCreate()       {          super.onCreate();                   //Set up the green room          //The setup is capable of getting called multiple times.         LightedGreenRoom.setup(this.getApplicationContext());           //It is possible that more than one service          //of this type is running.           //Knowing the number will allow us to clean up           //the locks in ondestroy.          LightedGreenRoom.s_registerClient();     }      @Override      public int onStartCommand(Intent intent, int flag, int startId)      {          //Call the IntentService "onstart"         super.onStart(intent, startId);                   //Tell the green room there is a visitor         LightedGreenRoom.s_enter();                   //mark this as non sticky          //Means: Don't restart the service if there are no         //pending intents.         return Service.START_NOT_STICKY;     }      /*      * Note that this method call runs      * in a secondary thread setup by the IntentService.      *        * Override this method from IntentService.       * Retrieve the original broadcast intent.      * Call the derived class to handle the broadcast intent.       * finally tell the lighted room that you are leaving.      * if this is the last visitor then the lock        * will be released.      */      @Override          final protected void onHandleIntent(Intent intent)       {                  try {              Intent broadcastIntent             = intent.getParcelableExtra("original_intent");              handleBroadcastIntent(broadcastIntent);          }                  finally {              LightedGreenRoom.s_leave();          }          }      /*      * If Android reclaims this process,      * this method will release the lock        * irrespective of how many visitors there are.       */      @Override      public void onDestroy() {          super.onDestroy();         LightedGreenRoom.s_unRegisterClient();     }  }

原创粉丝点击