Why won't my BroadcastReceiver call other methods? - android

I have a BroadcastReceiver that can successfully catch a broadcast, but if I try to call another method from it, it won't always work. Here's my setup:
private class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (working == true) {
//toast to make sure we got here
doWork();
}
}
}
The toast within the if gets called, but doWork doesn't execute. Has anyone dealt with a similar issue?

The Broadcast receiver doesn't have the same context and life cycle as your application, you can't do a lot of normal stuff in it. All your supposed to do is handle the event and return as quick as possible. In other words, start a service, or notify the user.
From: http://developer.android.com/reference/android/content/BroadcastReceiver.html
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.

Most likely it's not staying awake long enough. Are you using a WakeLock to keep Android from going back to sleep?
So, firstly you'll need to add this to your manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
and then in your receiver //pseudo code except for the parts that matter...
public WakeLock wl;
#Override
public void onReceive(c,i){
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);//this matters
wl.getWakeLock().acquire(); //as does this
//do whatever but DON'T do it here... call a Service like this...
Intent i = new Intent(context, MyExcellentService.class);
try {
PendingIntent.getService(context, 0,i, 0).send();
}catch(Exception e){}
//hold on to wl in some way and release it in the service, not here,
wl.getWakeLock().release();
}
If you're going to use a service you'll need to register it in your manifest like so...
<service android:name="MyExcellentService" />
And I would extend the Application and hold on to the WakeLock there, so that you don't have to pass it around in more convoluted ways. You can just reach up into the Application and talk straight to it!
You'll want to look up the differences in the *kinds of wakeups PARTIAL_WAKE_LOCK will not keep the screen awake, for example, just the background processing.
Let me know if this works, I'm cutting and pasting from various places, so, I may be overlooking something.

Related

Service-Activity Communication data processing while sleep

Project statement:
i have a simple counter app which has 6 things i am counting. on my wearable, i have a radiobutton group which selects which of those things i want to count. it then displays the current count for the item on the watch and then i can either add or subtract 1 from it from the watch. The watch is only an interface and interaction device. it does no processing. All processing of information and storing of information is done on the mobile. so the watch merely sends messages and displays information.
How it works:
the wear sends messages to the mobile via Wearable.MessageApi.sendMessage() and the phone responds with Wearable.DataApi.putDataItem(). The watch is sending multiple forms of informaiton like add/subtract/countRequest in addition to which item it is references. the mobile only responds with the item count requested and the watch only need change the display if it is a different value than what is showing.
This is a general messenger understanding question. I have
public class MyListenerService extends WearableListenerService{
#Override
public void onMessageReceived(MessageEvent me){
showToast();
}
}
The listener works. Now i want to do something useful with the listener because it is actually sending data i need. But i am having trouble communicating between Service and Activity because of my limited experience. I have read up on messaging and broadcast receivers. and am looking for ideas on how to implement to get my result or a better understanding.
From what i am gathering from trying to code, a service cannot directly interact with my interface, so i need to communicate with my activity in some way. 2 ways i have read is messaging(handlers) and broadcastreceivers.
each of these methods will do the function, however have their drawbacks which is where i am looking for better understanding or help.
for the Handler: even though i can create a static handler class and run code within the handler class, because it is static i cannot call non static objects which means if i try and do this it fails.
Service:
public class MyListenerService extends WearableListenerService{
#Override
public void onMessageReceived(MessageEvent me){
Activity.mHandler.sendEmptyMessage(MyConstants.COMMAND);
}
}
Activity:
public static Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
doThis(msg.what);
}
};
private void doThis(int command){
processCommand();
updateUserInterface();
}
Is there a way i can implement the above Handler, because when i process message if i just simply do a toast, it works, so i can receive the message. I just cant call a non-static object from a static. so i am assuming i cannot update an interface object either if i just threw the code of doThis() inside the Handler since i am just using it as a go between. When i was using LiveListeners and the onMessageReceived() was within my Activity, rather than a separate service, i just called doThis() directly and it worked great.
for the BroadcastReceiver:
There is a lot of code to show so instead i will just explain my issue for it and you may be able to answer from that. apparently you have to register/unregister on onResume()/onPause(). That would prevent me from being able to process information when the phone goes to sleep. the whole point of running a service is so i can do stuff when the phone is asleep or the activity is not in the foreground.
Before i was doing "LiveListeners" and it worked fine, so long as activity was in the foreground and phone was not sleeping. only way to have it work while sleeping is to engage a service to work in the background.
So my question is, what is best way to handle this situation so that i can process the information that the wearable is sending to the mobile while the mobile is asleep. or is there another method to send data i did not find?
If you extend WearableListenerService, you are creating a special Service which runs as part of your app's process. You can use this to communicate with another Service in your app which does all the processing, or use broadcasts (as you noted.) In either case, the Service is running in the context of your process and on the main thread - so if you need to do any heavy processing you'll need to offload it to a background thread.
Since your WearableListenerService is declared in the manifest and its lifecycle managed by Android Wear (by default), it's going to be simplest to either create a secondary Service or use a BroadcastReceiver to do your processing. Just note that "processing" must be lightweight if in a BR. If you use a BR, look into using LocalBroadcastManager as it is more efficient than sending the broadcast via the usual Context.sendBroadcast(). It's roughly the equivalent of sending a message to your app, it just happens to be in Intent form.
I certainly do not want to oversimplify greatly, but I like the easy way. Having intent I just awaken mobile or wearable from Sleep, and then the other threads also perforce awaken and process data.
#Override
protected void onHandleIntent(Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
String tmp = " ";
PowerManager.WakeLock wakeLock = null;
// wakeLock.acquire();
if (intent.getAction() != null) {
tmp=intent.getAction();
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock((PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP), TAG);
wakeLock.setReferenceCounted(true);
if(! wakeLock.isHeld()) {
wakeLock.acquire();
}
Code snippet from https://github.com/NickZt/E52/blob/master/wear/src/main/java/ua/zt/mezon/e52/core/MySpcIntentService.java

How do I run parallel actions in android?

How do I run a parallel action (process) to the main app in Android?
I know that, there are a lot of ways to do it: Threads, Tasks, Handlers and etc'...
This is the way I chose. But I think it takes a lot of memory and doesn't closes in the interrupt call.
checkReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO: Check is this a good way to handle threads
Thread t = new Thread() {
#Override
public void run() {
internetConnectionManager.TryConnect();
this.interrupt();
}
};
t.start();
}
}
};
Two things wrong with your arroach:
You should not start a thread in onRecieve method. The reason is explained here :
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
Second, calling Thread.currentThread().interrupt() does not make any sense in your example since your thread is already done by that line and will finish, and also because you don not check interrupted flag anyway.
The better way, in my opinion, would be to start a simple IntentService from your onReceive code. Here is a simple tutorial.
Important edit based on FunkTheMonk's comment:
If the broadcast comes from an alarm or external event, it is possible that your device will go to sleep shortly after onReceive returns (even if you create a service). If that is the case, instead of using regular BroadCastReceiver you should extend WakefulBroadcastReceiver from support library.
Use handler
if you want to stop handler then fire an intent with some value eg.("quit handler")to receiver
and call remove call back and inside handler you can handle the rest using ACTION switch
you can also use intentservice

How to send and get data between Service and BroadcastReceiver?

In my previous question: How to keep and access data in BroadcastReceiver? I've got excellent answers, but here I focus on this.
There is BroadcastReceiver and there is Service. I would like to send some data from the receiver to service and get the data back. The party which initiates the transfer is receiver, not the service.
For the sake of the simplicity, let's say that receiver waits for SCREEN_ON and then asks service "is this a birthday of the user", and server returns true or false.
If I am not mistaken I should attach my data that goes to Service to the Intent, and call startService, but how to get data back?
You can't really do this the way you've described. #CommonsWare is correct, your chosen architecture is flawed.
BroadcastReceivers are short-lived. Therefore they cannot make any asynchronous calls and they cannot perform any tasks that take a "significant amount of time". Your BroadcastReceiver gets triggered by a certain event and if you need to do any significant amount of work due to that event you will need to delegate that work to a Service.
Normally (for example: within an Activity), if you wanted to request information from a Service you could either bind to the Service and make a synchronous call to the service to request the data you want OR you could call the service asynchronously (ie: send an Intent to it) and then listen for the returned result (using a BroadcastReceiver). Neither of these methods works from a BroadcastReceiver because a BroadcastReceiver cannot bind to a Service and it cannot make asynchronous calls.
EDIT: Copied relevant information from the Android documentation for reference here
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.
Use a session!
Save the data in a session:
#Override
public void onReceive(Context context, Intent intent)
{
SessionManager session = new SessionManager(context);
// get user data from session
HashMap<String, Object> user = session.getUserDetails();
// name
m_userId = (int)user.get(SessionManager.KEY_ID);
m_context = context;
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "");
wl.acquire();
// Put here YOUR code.
//Toast.makeText(context, "Alarm !!!!!!!!!!", Toast.LENGTH_LONG).show(); // For example
wl.release();
}

How can I ensure that a CountDownTimer ticks and completes?

I have a widget that, when pressed, sends a broadcast that starts a CountDownTimer that updates my widget, and at the end plays an alarm. This works beautifully in the emulator.
On my phone, however, it's a different story. My phone is so resource constrained that my process is killed regularly, which, of course, means that the CountDownTimer no longer updates my widget.
It seems to be that the only way to reliably do stuff in the future is to use the AlarmManager, as this sets a system-level alarm. However, the documentation states, and I agree, that you're not supposed to use it for ticks. However, since anything else you're likely to use has the possibility of being shut down arbitrarily, they're not really giving us much choice.
My question is: is there a way to ensure that a CountDownTimer keeps ticking and finally calls onFinish(), or do I have to simply drop it and switch to AlarmManager, and "misuse" it? Any other options of guaranteeing that the thing ticks and finishes are also welcome.
I should add that I can't rely on the OS calling onUpdate(), both because it will do so no faster than every 30 minutes, and also because most of the time the widget just does nothing. It's only when it is clicked that it ticks down every second for a handful of minutes.
I think this is a solution.. Create a dummy service..
public class DummyService extends Service{
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
and start it from your activity using countdowntimer like this.. in oncreate of the activity
Intent intent = new Intent(this, DummyService.class);
startService(intent);
and also dont forget to declare this service in your manifest like this..
<service android:name=".DummyService" >
</service>
hope this helps..
What I ended up doing was moving from a BroadcastReceiver to a Service.
Not just using a dummy service, but having a real service embody what the BroadcastReceiver did previously.
As this won't guarantee that my timers won't get killed, if requested, android will automatically try to restart your service after it's been killed. In this event, I plan to have some code that will restore the running state from disk and continue.
To add to the accepted answer: from what I remember, the BroadcastReceiver only lives as long as it takes to process the broadcast.
So launching a timer or something from within the BroadcastReceiver will not work (as stated).
This is why it is recommended to use the BroadcastReceiver to launch a Service that will do the timing. Sure, the service can still be killed, but not as early as the BroadcastReceiver.

Using startForeground() with an Intent Service

I am trying to keep alive a service that reacts to screen on/off changes. The service would work perfectly for awhile, but then eventually it would be killed. I am now attempting to use startForeground() to keep the process alive, but it still seems to be dying. I understand that there is no way to keep a process alive forever, without error, but I feel like I must be doing something wrong, as adding startForeground() added no extra life to the process. Also, as a side note, Logcat complains about a leak, as unregisterReceiver() is not called (except manually by a button press from the user).. however, due to the nature of what I am trying to accomplish, the receiver needs to run until explicitly told to stop.
Any suggestions?
Relevant Code:
public class UpdateService extends IntentService {
public UpdateService() {
super(null);
}
#Override
protected void onHandleIntent(Intent intent) {
final int myID = 1234;
Intent notificationintent = new Intent(this, Main.class);
notificationintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendIntent = PendingIntent.getActivity(this, 0, notificationintent, 0);
Notification notice = new Notification(R.drawable.icon_image, "***********", System.currentTimeMillis());
notice.setLatestEventInfo(this, "*************", "***********", pendIntent);
notice.flags |= Notification.FLAG_NO_CLEAR;
startForeground(myID, notice);
boolean screenOn = intent.getBooleanExtra("screen_state", false);
// Blah Blah Blah......
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}
(Updated) I suppose there are the following possible cases:
1) documentation for IntentService states:
the service is started as needed, handles each Intent in turn using a
worker thread, and stops itself when it runs out of work.
So, it might be that your service is normally stopped after onHandleIntent() is finished (especially, as you mentioned that startForeground() added no extra life to the process).
2) You might try to check if it's somehow can be related to device going to sleep (or maybe you are starting your service by schedule and awkening device - in this case you might need to acquire WakeLock)
3) In the very rare cases, the system still can kill foreground process - so if you do a lot of allocations (really lot) and some other work in onHandleIntent() (instead of "Blah Blah Blah" at your code) - you might run into it - but I suppose it's not the case.
As question's title is "Using startForeground() with an IntentService" - would like to clarify that too:
I believe nothing (architecture, best practices, android framework, java docs for IntentService) prevents you from running your intent service as a foreground. Of course you need to thought out carefully its usage and whether you actually need a foreground service. Some ideas are available here. For sample code see below. (Sample code can end up showing multiple notifications if you queued multiple jobs/intents into IntentService, so there might be better solution depending on your need.)
public class ForegroundService extends IntentService {
private static final String TAG = "FrgrndSrv";
public ForegroundService() {
super(TAG);
}
#Override
protected void onHandleIntent(Intent intent) {
Notification.Builder builder = new Notification.Builder(getBaseContext())
.setSmallIcon(R.drawable.ic_foreground_service)
.setTicker("Your Ticker") // use something from something from R.string
.setContentTitle("Your content title") // use something from something from
.setContentText("Your content text") // use something from something from
.setProgress(0, 0, true); // display indeterminate progress
startForeground(1, builder.build());
try {
doIntesiveWork();
} finally {
stopForeground(true);
}
}
protected void doIntesiveWork() {
// Below should be your logic that takes lots of time
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
IntentService automatically shuts down when onHandleIntent() completes. It is to perform a brief bit of work when something occurs. It is not supposed to live for more than a few seconds, typically.
I am going to assume that this is tied to what I wrote in your last question in this area.
Something in the rest of your app will be registering and unregistering the BroadcastReceiver for the screen on/off events -- apparently, from your comments, it is an activity. If what you want to do when those things occur is very very quick (on the order of a few milliseconds), just do the work in onReceive(), and be done with it.
If, on the other hand, you have more work than a few milliseconds' worth, you will need to have that work be done by something else that can do the work on a background thread. For example, if the "something in the rest of your app" that registered the BroadcastReceiver is indeed an activity, the activity might just spawn an AsyncTask to do the work.
Another possibility is to use an IntentService. You elected to go down this path in your work prior to that last question. I do not know why. Regardless, an IntentService, like an AsyncTask, is supposed to be a short-lived component -- you send it a command via startService(), it does its work in onHandleIntent(), and it goes away.
With all that in mind, let's talk about your specific points.
The service would work perfectly for awhile, but then eventually it would be killed.
It is unclear what you think "killed" means. An IntentService automatically goes away once onHandleIntent() returns, and that ideally should occur within a handful of seconds.
I am now attempting to use startForeground() to keep the process alive, but it still seems to be dying.
Again, it is unclear what you think "dying" means. Bear in mind that the mere existence of an IntentService does not stop the CPU from shutting down once the screen turns off, and startForeground() has nothing to do with that.
Also, as a side note, Logcat complains about a leak, as unregisterReceiver() is not called (except manually by a button press from the user)..
You also need to unregister the receiver before the user exits the activity. It is usually a good idea to call registerReceiver() in onResume() and unregisterReceiver() in onPause().
If memory is low and your consuming too much memory and your sitting in the background for too long then you WILL be killed by the ActivityManager.

Categories

Resources