send broadcast with combination of localbroadcastmanager sendorderedbroadcast - android

I'm wanting to implement what CommonsWare describes on this blog post: http://commonsware.com/blog/2010/08/11/activity-notification-ordered-broadcast.html. The post makes sense, and I was able to browse the example source here: https://github.com/commonsguy/cw-advandroid/tree/master/Broadcast.
What I'm curious about is if calling LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast); inside of a service will still be picked up by a broadcast receiver of the type you define in your manifest.
In case what I'm asking isn't clear, what I'm trying to do is use the LocalBroadcastManager because the broadcasts from my service don't necessarily need to be seen system wide and I'd rather keep them private if possible, but I also want to display notifications if the user closes my app and the service is still running. Is there a way to combine both of those capabilities without sending a broadcast twice inside of the service?
(What I don't want to have to do) like:
LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast);
sendOrderedBroadcast(broadcast);

What I'm curious about is if calling LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast); inside of a service will still be picked up by a broadcast receiver of the type you define in your manifest.
No. LocalBroadcastManager only works with receivers registered with the LocalBroadcastManager singleton itself. Moreover, LocalBroadcastManager does not support ordered broadcasts, last I checked.
what I'm trying to do is use the LocalBroadcastManager because the broadcasts from my service don't necessarily need to be seen system wide and I'd rather keep them private if possible
So long as you are not using an <intent-filter> on your BroadcastReceiver in the manifest, and therefore are using an explicit Intent as the broadcast itself, your broadcast will only be seen by yourself and the bit of the OS that manages broadcasts. Other apps will not be able to spy upon it.

If you only have 2 objects that might handle your broadcast (in your case an Activity and a notifications controller), you can achieve the behavior of a ordered broadcast using only the LocalBroadcastManager.
The general idea is:
Set up your Service so that it broadcasts an Intent to your Activity with a particular action when you want to display your result
In your Activity create a BroadcastReceiver that handles your Service result Intent, and register it on the LocalBroadcastManager with an IntentFilter using the action from step 1
In your Service, when the results are available, try to send the result Intent using LocalBroadcastManager.getInstance(Context).sendBroadcast(Intent) this method returns a boolean that indicates if the broadcast has been sent to at least one receiver. If this boolean is false, it means that your Activity didn't handle your broadcast and you should show a notification instead.
In your service:
public UnzipService extends IntentService {
public static final String ACTION_SHOWRESULT = UnzipService.class.getCanonicalName() + ".ACTION_SHOWRESULT";
#Override
protected void onHandleIntent(Intent intent) {
Thread.sleep(500); // Do the hard work
// Then try to notify the Activity about the results
Intent activityIntent = new Intent(this, YourActivity.class);
activityIntent.setAction(ACTION_SHOWRESULT);
activityIntent.putExtra(SOME_KEY, SOME_RESULTVALUE); // Put the result into extras
boolean broadcastEnqueued = LocalBroadcastManager.getInstance(this).sendBroadcast(activityIntent);
if (!broadcastEnqueued) { // Fallback to notification!
PendingIntent pendingIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
.notify(SOME_ID, new NotificationCompat.Builder(this)
.setContentIntent(pendingIntent)
.setTicker("results available")
.setContentText("results")
.build());
}
}
}
In your Activity:
public YourActivity extends Activity {
private BroadcastReceiver resultReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
processResult(intent); // Results Intent received through local broadcast
}
}
private IntentFilter resultFilter = new IntentFilter(UnzipService.ACTION_SHOWRESULT);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate();
Intent intent = getIntent();
if (UnzipService.ACTION_SHOWRESULT.equals(intent.getAction())) {
// The Activity has been launched with a tap on the notification
processResult(intent); // Results Intent contained in the notification PendingIntent
}
}
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this)
.registerReceiver(resultReceiver, resultFilter);
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(resultReceiver);
super.onPause();
}
private void processResult(Intent intent) {
// Show the results from Intent extras
}
}
This should be a complete working example.
I hope this helps who is trying to implement ordered broadcasts with LocalBroadcastManager from support library!

I understand you want to achieve the following:
"I have an event that occurs in the background. I want to update my activity, if the activity is on the screen. Otherwise, I want to raise a Notification." (#TheCommonsBlog)
You can achieve this behaviour by implementing a ResultReceiver.
Examples Restful API service and
http://itekblog.com/background-processing-with-intentservice-class/
What you basically do is instance a ResultReceiver in your Activity and pass it to the Service like a Parcelable parameter through an intent. Then, each time your service whats to update the UI, the service verifies the ResultReceiver object for NULL. If not NULL, you update the Ui via the onReceiveResult interface. Else, you raise a notification. When your activity dismisses, make sure you set the ResultReceiver on the Service to NULL.
Hope it helps.
PS: IMO, broadcasts are too much work and hard to control.

Use LocalBroadcastManager and broadcasts become easy to use.
I am not in favor of updating an Activity if an event occurs in the background. The user might already be doing something else in the Activity. Seems to me that a Notification is sufficient; it's always visible and remains until the user dismisses it. Gmail and Gcal work like this; Gmail doesn't update the current screen if a new mail comes in. If you want to know how to handle the task flow for handling a notification when the user is already in the app, see the Notifications API guide and also the [Notifying The User2 training class.

Related

Be notified when Service is started

I have a Service that scans for BLE devices. The Activity should show some data gathered by the Service.
A Receiver has been implemented, to be notified when the Bluetooth is enabled, so that we know when to start the Service.
If the Service is running, and the Activity is opened, it just executes bindService(). However, if the Service isn't running (because the Bluetooth is disabled), the App is opened and the Bluetooth is enabled, it won't bind because the binding process has already been skipped.
How can I be notified about the Service starting or automatically binding when started?
Thank you.
You can use the LocalBroadCastManager to send a broadCast from your service to your activity.
Helper to register for and send broadcasts of Intents to local objects within your process. This has a number of advantages over sending global broadcasts with sendBroadcast(Intent):
You can use localbroadcast reciever from your service.
In your service use these code
intent = new Intent("my-integer");
intent.putExtra("message",""+a);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
In your activity use this code
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Extract data included in the Intent
String data = intent.getStringExtra("message");
if (!data.equals("0")) {
//Do something
} else {
//Do something else
}
}
}
};

how to stop a service/thread when the application exits or from a different activity

now i have a server class that i run on a thread from an activity(i.e servActivity).now when i am not interacting with my app in anyway possible (like i have removed it from recent apps etc) the thread should stop which currently is not stopping. So i researched and i found that i should use a bound service. now a bound service i will have to bind it to servActivity and when servActivity is destroyed i have to unbind and stop service but i dont want to do that. i want to stop service when i am not interacting with the app. i also found that maybe i have to extend application class but cannot find the solution to achieve this?Is it advisable to extend the application class?
i want to be able to create a service running on independent thread from a particular activity(ie servActivity) and then be able to interact with the service from any activity and service should be active (even if the activity in which i started the service i.e-servActivity is destroyed by going to previous activity etc) through button or whatever until i am not interacting with the app(i have a notification controller which also needs to be closed to stop the interaction)
i have a client class on one device whose object i create again and again if i have to make request but i want to make only one object for server class because it has a while(true) loop so it keeps running so i want to be able to interact with the server from all activities and stop it when i am not interacting with the application
i also found a way in which i can make an abstract class which extends activity and extend that derived class to all the other activities in my app.But how to i bind the service to all the other activities in the class so that i can interact with the service from all the other activities?And how would i know that if all activities and notification controller have been stopped and there is no interaction with user?something like this how to know our app has gone to background in android
If there is there any other method please suggest
Please help
thanks in advance
You can create a BroadcastReceiver in your Service Class to interact/start/close your Service from any Activity or even from any App.
Your Activities can broadcast custom Action Strings which can be picked up by any BroadcastReceivers (even ones set up in Services) and thereby invoking their onReceive() methods allow communication.
1) I suggest you don't bind your Service to any Activity and instead use Intent to initiate it in your Activity like this....
//In your Activity
Intent i = new Intent(this, /*MyServiceClassName.class*/);
startService(i);
Or else your Service may still be active until you unbind it.
2) Create a BroadcastReceiver in your Service Class to listen for certain Action Strings broadcasted by your Activities....
//In your Service
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(/*"Action String to stop Service"*/)){
stopSelf();
}else if(action.equals(/*"Action string to interact with Service"*/)){
//Do what you want
};
3) Now set what Action Strings the Broadcast Receiver will listen for and also register it in your Service onCreate() method....
//In your Service onCreate() method
IntentFilter filter = new IntentFilter();
filter.addAction(/*"Action String to stop Service"*/);
filter.addAction(/*"Action String to do something"*/);
registerReceiver(receiver, filter);
4) And also unregister your receiver when Service onDestroy() is invoked as housekeeping....
//Service onDestroy()
#Override
public void onDestroy(){
super.onDestroy();
unregisterReceiver(receiver);
}
5) Finally broadcasting Action Strings from your Activities through Intent....
//From any Activity
Intent intent = new Intent(/*"your custom Action String that should match
up with whats set up with the BroadcastReceiver in Service"*/);
sendBroadcast(intent);
6) So once the broadcast is sent your receiver should pick it up then its onReceive() method will be invoked. Therefore you now have a medium for your Activities and Service to communicate through and also the Service will persist even after you close your app until you stop it explicitly with....
//From any Activity
Intent i = new Intent(this, /*MyServiceClassName.class*/);
stopService(i);
7) Stop service when app is stopped....
//In all your activities
#Override
protected void onDestroy() {
Intent i = new Intent(this, /*MyServiceClassName.class*/);
stopService(i);
super.onDestroy();
}
8) First you'd need to put a killcode intent action String in your Service as demonstrated in points 2 and 3 then put this code in your app's Activity onPause() methods....
#Override
protected void onPause() {
PendingIntent pintent = PendingIntent.getBroadcast( this, 0, new Intent(/*"Action String to stop Service"*/), 0 );
AlarmManager manager = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
// set alarm to fire 10mins (1000*60*10) from now (SystemClock.elapsedRealtime())
manager.set( AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000*60*10, pintent );
super.onPause();
}
And this in your app's Activity onResume() methods....
#Override
protected void onResume() {
PendingIntent pintent = PendingIntent.getBroadcast( this, 0, new Intent(/*"Action String to stop Service"*/), 0 );
AlarmManager manager = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
manager.cancel(pintent);
super.onResume();
}

Why would LocalBroadcastManager not work instead of Context.registerReceiver?

I had to implement a feature to this app which consists of an Activity and a Service working on the background (it implements Service, not IntentService).
I went through a few tutorials on the Internet that are supposed to work, and they all use LocalBroadcastManager, which by the way is the recommended by Android:
If you don't need to send broadcasts across applications, consider
using this class with LocalBroadcastManager instead of the more
general facilities described below.
I literally lost a day to find out the problem why it wouldn't work for me: it only works if I use Context.sendBroadcast(). and Context.registerReceiver() instead of the LocalBroadcastManager methods.
Now my app is working, but I feel I am going against the best practices, and I don't know why.
Any ideas why it could be happening?
EDIT:
After I wrote this question I went further on the problem. LocalBroadcastManager works through a Singleton, as we should call LocalBroadcastManager.getInstance(this).method(). I logged both instances (in the Activity and in the Service) and they have different memory addresses.
Now I came to another question, shouldn't a Service have the same Context as the Activity that called it? From this article a Service runs on the Main Thread, hence I'd think the Context would be
the same.
Any thoughts on that? (sorry for the long post)
Code samples:
MyService
public class MyService extends Service {
...
// When an event is triggered, sends a broadcast
Intent myIntent = new Intent(MainActivity.MY_INTENT);
myIntent.putExtra("myMsg","msg");
sendBroadcast(myIntent);
// Previously I was trying:
// LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(myIntent);
}
MyActivity
public class MainActivity {
...
private BroadcastReceiver messageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("onReceive", "received!");
// TODO something
}
};
#Override
protected void onResume() {
super.onResume();
registerReceiver(messageReceiver, new IntentFilter(MY_INTENT));
// Previously I was trying:
// LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(messageReceiver, new IntentFilter(MY_INTENT));
}
}
I've never used LocalBroadcastManager, but it sounds like you have to register your receiver on there (i.e. lbm.registerReceiver(...), not mycontext.registerReceiver(...)). Are you doing that?
Now I came to another question, shouldn't a Service have the same Context as the Activity that called it? From this article a Service runs on the Main Thread, hence I'd think the Context would be the same.
The Context class is not related to threads. In fact, both Service and Activity are (indirect) subclasses of Context -- so they're their own Contexts! That's why you can use "this" as a Context.
But regardless of which context you send into LocalBroadcastManager.getInstance(), you should be getting the exact same LBM instance out. I can't think of any reason that you wouldn't -- except if you're running the Activity and Service in different processes?
Declaration:
private BroadcastReceiver receiver;
Initialization:
receiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
//todo
}
};
Registration:
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, new IntentFilter("RECEIVER_FILTER"));
context can be any type of Context, you can use the application context.
Unregister:
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);
Broadcast:
Intent intent = new Intent("RECEIVER_FILTER");
intent.putExtra("EXTRA", someExtra);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
check out if your Service and Activity are run in different process, LocalBroadcastManager can't apply in different process.(you should see it in AndroidManifest.xml file)

How to send message from BroadcastReceiver to activity or fragment

I have a receiver, it does call details saving task like storing incoming call, outgoing call etc.. all these details goes to sqlite DB. If my activity is not running, then its fine.
Sometime, when my activity is running, i get some incoming call. the receiver runs & stores data to DB. UI wont get refreshed because it never knows about change in DB.
Here i need to manually tell from receiver that, if activity is running refresh screen. How to implement this process in android.
I'm slightly confused in this part
You can use a LocalBroadcastManager to send a local broadcast to your Activity (more efficient and more secure than using a global broadcast):
Intent intent = new Intent(action);
LocalBroadcastManager mgr = LocalBroadcastManager.getInstance(context);
mgr.sendBroadcast(intent);
http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html
Your Activity would have to register a BroadcastReceiver in onStart and unregister it in onStop:
private BroadcastReceiver mBroadcastReceiver;
mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// do your thing
}
};
LocalBroadcastManager mgr = LocalBroadcastManager.getInstance(this);
mgr.registerReceiver(mBroadcastReceiver, new IntentFilter(action));
in onStop:
mgr.unregisterReceiver(mBroadcastReceiver)
Now that's the official Android way to do it. I most certainly prefer to use an event/message bus like Otto or EventBus (https://github.com/greenrobot/EventBus). You can use those to broadcast messages/events across different components in your app. The advantage is you don't need access to a Context (like you do when using Broadcasts), it's faster and it forces the developer to object oriented programming (since the events are always objects). Once you start using an event bus you'll never look back to local broadcasts and you'll replace many of the sometimes messy observer / listener patterns used across your app.
You can create a BroadcastReceiver inside an activity. Register it in onResume() and unregister it in onPause(). Whenever your other receiver receives a broadcast, send a broadcast to this receiver too. If the activity is running(i.e. on front), the broadcast will be received. Do whatever you want in its onReceive().
Example:
BroadcastReceiver br = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Do stuff
}
};
Also override methods:
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(br);
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(br, new IntentFilter("intent_filter"));//Use any string for IntentFilter you like
}
You can update fragments from activiy by creating methods inside fragment and access them from Fragment object inside activity.

How can I tell if my activity was started by a broadcast receiver?

I have an alarm clock application I am making. I have one activity where the user sets the time for the alarm. This registers a broadcast receiver class I have made to receive a broadcast at the time of the alarm, using AlarmManager. I then start a new activity in the receivers onReceive(). When this second activity starts, the alarm clock sound is played in onStart(). How can I tell if my activity has been started by a receiver or if the user is just multitasking with the application? I don't want my sound to play when the user silences the alarm, presses the home button, and then renters the app (while still on the sound playing activity).
Just send an extra via the intent you use in your onReceive() method:
Intent intent = new Intent(this, NextActivity.class);
intent.putExtras("playSound", true);
in your "sound playing" activity, you have to play the sound in onCreate():
boolean playSound = getIntent().getBooleanExtra("playSound", false);
This will return false if the intent-extra "playSound" does not exist or is set to false, true if it is set to true.
onCreate() is only called once (when the activity starts), while onStart() gets called everytime a user reenters your activity (i.e. through recent apps). You can see this in the lifecycle:
(diagram source)
Where Paused is called when something draws over you activity (e.g. low battery dialog), Stopped is called if you "exit" your app (e.g. through the home-button).
Start an activity or a service, etc., based on a received broadcast then you need a standalone broadcast receiver and you put that in your android manifest file. If you want your activity itself to respond to broadcasts then you create an instance of a broadcast receiver in your activity and register it there.
public class BRActivity extends Activity {
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
..................
..................
}
};
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(BROADCAST_ACTION);
this.registerReceiver(this.broadcastReceiver , filter);
}
public void onPause() {
super.onPause();
this.unregisterReceiver(this.broadcastReceiver );
}
}
So, this way the receiver is instantiated when the class is created (could also do in onCreate). Then in the onResume/onPause I handle registering and unregistering the receiver. Then in the reciever's onReceive method I do whatever is necessary to make the activity react the way I want to when it receives the broadcast.
You can do as below:
For each alarm user sets, you put an boolean flag in sharedpreference to true. E.g. you have three alarms then in sharedpreference you will have 3 flags.
Now suppose a alarm broadcast is received for alarm1 and activity2 is started.
Now in activity2 first thing you check is whether the flag for alarm1 which you set in sharedpreference is true or false, if true play sound.
When user silences the alarm or press home button then you can mark this flag to false, so next time if user starts activity from background, flag in sharedpreference will be false and sound will not be played.
Same thing you can achieve using sqlite db, by setting flags in sqlite db table instead of sharedpreference.
For the intent used to launch the sound playing activity use the FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS flag. So if the user moves out of the activity, it cannot be resumed.
Intent intent = new Intent(context, SoundActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
or in manifest
<activity
android:name="SoundActivity"
android:excludeFromRecents="true" >
</activity>
There are couple of solutions:
(1) one approach is to use a Singleton Class which can be shared across activity.
In this approach set a boolean flag of Singleton class in BroadcastReceiver and then check that flag in the activity (which is fired from your BroadcastReceiver) where you play sound. Please reset the flag if it is set. This solution assumes that the Broadcast receiver is part of your Android App package.
(2)
Alternatively, you can also use Intet.putExtra ("Key", Value) method when you start an activity from BroadcastReceiver. You can check this key in the Activity you started from BroadcastReceiver to know who started this activity.
So this will take care of detecting where you come from.
If you are simply trying to set single (one-shot ) alarm then creating another activity for playing the sound is OK. If you set repeat alarm ( alarm plays at a multiple interval), I am not sure how your application will behave.
I prefer to play the sound in the Broadcast receiver itself (registered as remote receiver in manifest) for a specified duration for a given alarm ( like 30 sec of sound or you can ask user to configure it).
So this way you can use the same BroadcastReceiver for playing sound for single-shot & multi-repeat alarm.
I will use the same PendingIntent for setting both Single-shot and multi-repeat alarm.
You can simply set flag or any value in Intent that will determine what's your purpose in that class ..
For Ex: For playing sound set a Boolean value to TRUE in Intent and send same over that class using bundle ..
Else
Set that Boolean value to FALSE if starting alarm class from some other class.
This is the code to find if app is started via broadcast receiver.
public class AlarmReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String AlarmTriggerString = (String)intent.getSerializableExtra("AlarmTrigger");
Intent i = new Intent();
i.setClassName("com.prasath.viki.bot","com.prasath.viki.bot.MainActivity");
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT|Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("FromBroacastReceiver",true);
i.putExtra("AlarmTrigger",AlarmTriggerString);
context.startActivity(i);
}
}
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
boolean FromReceiver = (boolean)getIntent().getSerializableExtra("FromBroacastReceiver");
String AlarmTriggerString = (String) getIntent().getSerializableExtra("AlarmTrigger");
if(AlarmTriggerString != null && FromReceiver != null && FromReceiver == true)
{
// do something
}
}

Categories

Resources