I find that when a BroadcastReceiver starts a Service in onReceive() method, the return of onReceive() will happen before the onCreate() of the Service being invoked. Why does this happen in this order?
The example code is below: BroradcastReceiver's onReceive() method:
public void onReceive(Context context, Intent intent) {
Log.i("=====Receiver=======","===== onReceive starts===="); // (1)
intent.setClass(context, AService.class);
context.startService(intent);
Log.i("=====Receiver=======","===== onReceive ends===="); // (2)
}
Service's onCreate() method:
public void onCreate(){
Log.i("=======Service========", "========OnCreate()=== "); //(3)
// some logic here
}
The log order is (1)(2)(3),not (1)(3)(2).
Could anybody help explain it?
The service has its own thread, so I would guess it doesn't get to run until the thread executing onReceive yields. It could also be that startService posts an intent, but again it doesn't get handled until onReceive returns. I don't know the actual implementation, but I do know that startService is an asynchronous call, so you shouldn't be depending on the order of execution rrlated to onReceive.
Related
I had setup AlarmManager in my MainActivity class.
A class called AlarmReceiver gets fired up for every set interval of time.
I have to perform an operation when that class is fired up. That code is in in another class Parsing.java
Now in AlarmReceiver.java, I'm doing this :
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Parsing obj = new Parsing(context);
obj.execute();
}
}
I cannot write the code directly in AlarmReceiver.java, because AlarmReceiver.java is already extending BroadcastReceiverand my code which is Parsing.java is extending another class.
So, I'm creating an object for Parsing class and calling that method.
Is my approach correct?
I'll furnish further information in case needed.
Please let me know if my approach is correct?
Thanks in advance!
EDIT:
Parsing.java
public class Parsing extends AsyncTask<Void, Void, Void> {
//some code
}
Starting an AsyncTask from a BroadcastReceiver is wrong for two reasons:
1. The thread on which onReceive() runs is terminated after the method returns, effectively ending any long-running task which may have been started from there. To quote the official docs:
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 ..... 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.
2. The Context instance that onReceive() provides is not the same as
the Context of an Activity or Service, i.e. Activity.this or
Service.this. You need that proper Context for performing many of
the common useful operations that we usually do from an Activity or
Service. So, for example, the correct way to start a Service in
onReceive() is:
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context.getApplicationContext(), ParsingService.class);
context.getApplicationContext().startService(i);
}
and not
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, ParsingService.class);
context.startService(i);
}
I don't know how you wrote your Parsing.java, it looks fine but remember this
This method is always called within the main thread of its process, unless you explicitly asked for it to be scheduled on a different thread using registerReceiver. When it runs on the main thread you should never perform long-running operations in it (there is a timeout of 10 seconds that the system allows before considering the receiver to be blocked and a candidate to be killed). You cannot launch a popup dialog in your implementation of onReceive()
To me, i think it's a better way to handle this is calling another service inside onReceive method, like this
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, ParsingService.class);
context.startService(i);
}
In my Android project, I have a normal Service:
public class MyService extends Service{
#Override
public int onStartCommand(...){...}
...
#Override
public void onDestroy() {
super.onDestroy();
Log.d("MyApp","MyService onDestroy() is called!");
}
}
In my BroadcastReceiver class, I stop MyService & do another task :
public static class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.stopService(new Intent(context, MyService.class));
doAnotherTask();
}
}
According to my log, onDestroy() of MyService is executed after doAnotherTask() is done.
How can I guarantee that onDestory() of MyService is executed before doAnotherTask() get called?
P.S.: I thought I could do something like:
boolean isStopped = context.stopService(new Intent(context, MyService.class));
if(isStopped){
doAnotherTask();
}
But it could be possible that there is no service has been started, which means stopService(...) does nothing. So, I can't rely on my above code.
call startActivityForResult() .... and after you got the onActivityResult,.... call your method doAnotherTask()
i think that will do the job
How about sending a special intent to your broadcast receiver from the onDestroy() function? When your receiver gets it, then call doAnotherTask(). (I am assuming that you can't simply call doAnotherTask() from onDestroy() directly.)
send a broadcast in the service's onDestroy function and in it's observer do your after things
The call to stopService() is asynchronous. You are basically telling Android that you want it to stop the Service. You have no control over when this actually occurs.
If you need to trigger something AFTER the Service is destroyed, then you send a broadcast Intent in MyService.onDestroy() and use that to trigger whatever you want to happen when the Service is destroyed.
Is onReceive() method of BroadcastReceiver thread safe or I need to implement synchronization on my own?
If I have any class level variable which is being used inside the onReceive() method, and the onReceive() method is called multiple times very quickly, would it cause an issue?
public class MyBroadCastReceiver extends BroadcastReceiver {
boolean isFirstTrigger = true;
#Override
public void onReceive(Context context, Intent arg1) {
if(isFirstTrigger)
{
//Do something time consuming
isFirstTrigger = false;
}
}
Is onReceive() method of BroadcastReceiver thread safe or I need to implement synchronization on my own?
It will only ever be called on the main application thread. Hence, it is thread-safe with respect to anything else running on the main application thread.
If I have any class level variable which is being used inside the onReceive() method, and the onReceive() method is called multiple times very quickly, would it cause an issue?
If the BroadcastReceiver is registered in the manifest, a new instance is created for each broadcast. This is why you do not normally see data members on a BroadcastReceiver.
I have a dynamic broadcast receiver registered in a service and my service is doing some heavy sdcard read/write operation in a while(somecondition) loop.
When a broadcast is sent from my another Application (which is in other process) is not received by my broadcast receiver.
This same broadcast is received when it is not executing while loop.
I also tried to put end of loop with Thread.Sleep(100) just to give some time for broadcast receiver to get executed but it is not working.
Any help regarding this will help me a lot.
-Thanks & regards,
Manju
Code below for registering BxRx:
this.registerReceiver(myReceiver, new IntentFilter(ACTIVITY_NAME));
code below for sending broadcast:
Intent intnt = new Intent(ACTIVITY_NAME);
intnt.putExtra("STOP_ALL_TESTING", true);
Log.d(TAG,"Sending BX STOP_ALL_TESTING");
myActivity.this.sendBroadcast(intnt);
code below for while loop:
while(somecondition){
:
:
:
Thred.sleep(100);
}
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"Received intent: "+intent.getAction());
boolean flag = intent.getBooleanExtra("STOP_ALL_TESTING", false);
Log.d(TAG,"Flag set to: "+flag);
if((boolean)intent.getBooleanExtra("STOP_ALL_TESTING",false)){
Log.d(TAG,"Broadcast received to STOP_ALL_TESTING");
Log.d(TAG,"Bx Rx, setting flag to stop testing as requested by user");
synchronized(this){
bStopTesting=true;
}
}
}
Please paste your complete code.
It looks like your problem is that you have an endless loop in service's onStartCommand method. Both onStartCommand and onReceive are executed on the same thread and only one after another. Applications main thread is a Looper thread, which handles events in a sequential manner. Basically, if you have an endless operation in the service, you will block the whole main thread, which includes all the GUI, services and Broadcast receivers. Calling Thread.sleep() won't help, because the method does not return. To avoid this, you can use IntentService http://developer.android.com/reference/android/app/IntentService.htmlclass, which will handle intents on another thread.
public class HeavyService extends IntentService {
public HeavyService() {
super("HeavyService");
}
#Override
public void onCreate() {
super.onCreate();
//do your initialization
}
#Override
protected void onHandleIntent(Intent intent) {
//this will be executed on a separate thread. Put your heavy load here. This is
//similar to onStartCommand of a normal service
}
}
I try to implement an IntentService with a BroadcastReceiver that reacts on the SCAN_RESULTS_AVAILABLE_ACTION.
The IntentService is supposed to compare Lists whenever onReceive is called. I always get the
"Service has leaked IntentReceiver"
error even though I unregister the BroadcastReceiver in onDestroy().
Here is the code:
public class MyClass extends IntentService {
private HashMap<String, List<String>>;
private WifiManager mWifiManager;
private WifiReceiver mWifiReceiver;
public MyClass() {
super("MyClass");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
mWifiReceiver = new WifiReceiver();
registerReceiver(mWifiReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY,"ScanLock");
mWifiManager.setWifiEnabled(true);
return START_NOT_STICKY;
}
#Override
public void onDestroy(){
unregisterReceiver(mWifiReceiver);
mWifiManager.setWifiEnabled(false);
}
#Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
}
class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Here I do my stuff with the scan results
//should be called every 5 seconds
}
}
Where is the problem in the code?
Why do I keep getting this error?
I still have to learn a lot about Android, but I think the IntentService is the right way to go since I do not expect any result from this class. It should just stop when I send a call stopService(). This IntentService is called by another IntentService! Is that a problem?
Thanks for helping.
I try to implement an IntentService with a BroadcastReceiver that reacts on the SCAN_RESULTS_AVAILABLE_ACTION.
This is largely pointless. Your receiver will be registered for a few seconds at most, hopefully.
I still have to learn a lot about Android, but I think the IntentService is the right way to go since I do not expect any result from this class.
That makes no sense whatsoever. You use an IntentService when you have a short bit of work that needs to be performed in a background thread. For example, if you use AlarmManager to check for new email messages every 15 minutes, or you have an activity kick off a large file download, you would use IntentService.
It should just stop when I send a call stopService().
You never call stopService() on an IntentService. The IntentService stops itself once onHandleIntent() returns. This is why your BroadcastReceiver will be removed within seconds -- your onHandleIntent() should only be running for seconds.
This IntentService is called by another IntentService!
This is unlikely to be a good design.
Try registering BroadcastReceiver in OnCreate() instead of OnStartCommand(),
That should fix your problem.