How to send and get data between Service and BroadcastReceiver? - android

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();
}

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

Android - starting IntentService frequently

In my application, a single instance of a class acts like a state machine. Many activities wants to receive updates on the state of this object. The state of this object itself is updated from data from a broadcast of some primitive information.
I have implemented it this way.
I registered a broadcast receiver in the manifest file which receives the primitive information and then starts an IntentService just passing the received information.
#Override
public void onReceive(Context context, Intent intent) {
Intent update_service = new Intent(context, StateMachineUpdateService.class);
update_service.putExtra(PRIMITIVE, intent.getDoubleExtra(PRIMITIVE, 0));
context.startService(update_service);
}
The StateMachineUpdateService keeps an instance and updates it:
#Override
protected void onHandleIntent(Intent intent) {
statemachine.update(intent.getDoubleExtra(PRIMITIVE, 0));
Intent broadcast = new Intent(STATE_MACHINE_UPDATE);
broadcast.putExtra(STATE_MACHINE_STATE, statemachine.get_state());
this.sendBroadcast(broadcast);
}
}
I am wondering if there is an more elegant way to achieve this. I am wondering if starting an IntentService (which in turn will start a separate thread) is something I should try to avoid. The primitive broadcast is sent about 10 times in a second.
Side-question:
let us say statemachine.update takes so much time that the next broadcast has arrived, I would like to ignore those broadcasts. What is the best way to do this?
If you have a singleton object that requires serialized access to its members, one way to go is to use a HandlerThread and schedule incoming work on it from your receiver or wherever else work may originate. The HandlerThread will process everything (runnable or message) in the order it was received (just like the Android main thread).
If you want to skip incoming items of work, you can record the receive time of the work and compare it to the actual time it's being executed, and skip it if the some threshold has been exceeded.
Starting IntentService takes 1-15 ms on my 1 GHz phone. As it is created on UI Thread 10 calls can take even 100 ms which is too much (user will experience lag). You should consider using Hanlder with WorkerThread. You can check how IntentService use this solution and implement it in your case.

Android update Activity from a Service

I'm so lost with all that workflow of notifications and services in Android. My sceneario is this:
I have an Android application that communicate to a MySQL database through a web-service using JSON-RPC. The data retrieved from the service will be displayed in the application.
The data will get updated over time, so the application needs to listen for changes of this and, if a change occur, show a notification and update the data displayed in the app.
To listen for changes I will need to run an "infinite"(until the app is destroyed or maybe until the app destroys it) thread that from time to time will call a method on th web-service which will return the changes since the last check.
UPDATE: Ok, I have been trying using Service and IntentService, but non of them fits my needs: a Service execute in the Main Thread, so If I perform an infinite loop there my app will freeze, IntentService has it's own worker thread but there is no comunication with the App, and I need it, or at least I need a way to know if the app is in foreground (in this case the notification will not popup but the data will be passed and updated) or in background (int this case the notification will pop up and on click it will direct the user to the app with the updated data)
#1 You can fire a broadcast message from your Service and define a Broadcast receiver in your Activity to receive this broadcast.
SEND BROADCAST-from Service
Intent i = new Intent("ALERT_CHANGE");
i.putExtra("DATA","News");
sendBroadcast(i);
RECEIVE BROADCAST-in Activity
registerReceiver(uiUpdated, new IntentFilter("ALERT_CHANGE"));
private BroadcastReceiver uiUpdated= new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent)
{
String DATA = i.getStringExtra("Data");
txt.settext(DATA);
}
};
Ok, after a lot of testing and thanks to the info given here I finally found a way to handle with the issue, so I will share it here:
On the IntentService I have a public static AtomicBoolean to control the end of the loop and be able to stop the service.
Then to determine if the Activity is in foreground or not I use the method suggested here https://stackoverflow.com/a/5504711/3107765
With the difference that I use the static modifier there, so I can check it from the service.
if the activity is in foreground I send a broadcast as it was suggested here by Eu. Dr. otherwise I use a notification that once clicked will let the user to the activity.

Downloading data using IntentService - Lifecycle changes

Tl;dr How to know when an IntentService has finished downloading upon returning to the Activity which listens to its result using a BroadcastReceiver?
I'm moving to implementing data downloading to IntentServices, and notifying when the task has finished using BroadcastReceivers.
I generally start the service in my Activity:
IntentFilter intentFilter = DownloadDataService.startDownloadData(this);
getLocalBroadcastManager().registerReceiver(mBroadcastReceiver, intentFilter);
The part that starts the Service:
/**
* Starts the service and returns an IntentFilter to subscribe a BroadcastReceiver on.
* When the task has finished, a broadcast for returned IntentFilter is sent,
* containing downloaded data.
*/
public static IntentFilter startDownloadData(final Context context) {
Intent intent = new Intent(context, DownloadDataService.class);
intent.setAction(ACTION_DOWNLOAD_DATA);
context.startService(intent);
return new IntentFilter(ACTION_DOWNLOAD_DATA);
}
And of course, onHandleIntent(Intent) (simplified):
#Override
protected void onHandleIntent(final Intent intent){
Data data = downloadData();
Intent intent = new Intent(ACTION_DOWNLOAD_DATA);
intent.putExtra(DATA, data);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
This all works well, and I can keep states in my Activity to know for example after an orientation change whether I was waiting for a download data result:
#Override
public void onResume(){
super.onResume();
if (mState == State.DOWNLOADING) {
Log.v(TAG, "Registering receiver for existing download data");
IntentFilter intentFilter = DownloadDataService.getIntentFilter();
getLocalBroadcastManager().registerReceiver(mBroadcastReceiver, intentFilter);
}
}
Great, now I can also handle orientation changes. Only one problem left:
Activity starts the DownloadDataService
User moves away from the Activity
DownloadDataService broadcasts its done message (which is not received by the Activity due to unregisterReceiver in onStop())
User moves back into the Activity
Activity still thinks it's waiting for the DownloadDataService, and does nothing.
How can I compensate for this?
Note that I do not have any persistence like databases for storing the downloaded data. The Activity retrieves the data from the broadcasted Intent.
Note #2: There is this answer to the question of how to know whether a Service is running. Although this might work, it is explicitly stated that that method is for debugging or implementing service management type user interfaces.
use sendStickyBroadcast to send a sticky broadcast. This broadcast is held by the system.
I wasn't really convinced by using SharedPreferences, static variables and other 'hacky' solutions.
I did find however that you can supply a ResultReceiver - which is parcelable - which you can use to notify your task is finished. It receives a Handler to specify the thread the result is handled on.
The advantage of this, is that you can save the ResultReceiver during onSaveInstanceState. Using some clever tricks you can certainly make this work. I have created an experimental library which facilitates this tactic: https://github.com/nhaarman/Ergo

Why won't my BroadcastReceiver call other methods?

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.

Categories

Resources