Pass additional parameters to a dynamically registered BroadcastReceiver.
The problem is basic: I want to pass parameters to a BroadcastReceiver. Can this be done? Even when the receiver is created dynamically?
Additionally, say I create an anonymous BroadcastReceiver i.e. as a variable implementation. Can I reference the encapsulating class variables? Check the code below for how I stop / start the file observer.
// Create the external media broadcast receiver.
mExternalMediaBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
// if action = media removed, stop the file observer.
EncapsulatingFragment.this.mFileObserver.stopWatching()
}
};
Is this valid?
Is this valid?
So long as the receiver has the same lifetime as EncapsulatingFragment.this, probably.
I want to pass parameters to a BroadcastReceiver. Can this be done? Even when the receiver is created dynamically?
Create an actual class and pass in the values to the constructor:
class WhateverReceiver extends BroadcastReceiver {
FileObserver mFileObserver;
WhateverReceiver(FileObserver observer) {
mFileObserver = observer;
}
#Override
public void onReceive(final Context context, final Intent intent) {
// if action = media removed, stop the file observer.
mFileObserver.stopWatching();
}
}
Then, in your fragment:
mExternalMediaBroadcastReceiver = new WhateverReceiver(mFileObserver);
All that being said... you might want to consider whether this logic should be implemented in your fragment. I/O-related stuff ideally lies outside of a fragment, such as in a repository object.
Related
I want to know how I can send an object of a class in fragment ,to broadcast receiver.
When I call object directly with fragment object , it is always null.
Example :
Here I create a receiver class. this class handles custom
Notification layout events . everything works correct except
for a record.pause(). MediaRecorder object is always null in this receiver ,while in the Sample_Fragment which records audio it is not. hence recording does not stop.
Receiver Class :
public class Sample_Receiver extends BroadcastReceiver {
Sample_Fragment fragment = new Sample_Fragment();
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ("pause".equalsIgnoreCase(action)) {
record.pause();
}
}
}
Update :
I could fix the problem through inner BroadcastReceiver.
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)
In my Android application, I have a simple list view with adapter. There's a heavy query which is to fill the list view with data. So I put it to an IntentService that runs in another thread.
The IntentService is normally running separately, on its own, just to query some data and insert it into the SQLite database.
But now I would like to have the following possibility:
The activity starts the IntentService with startService().
The IntentService does its heavy work.
When the IntentService is finished, it should inform the activity about the result so that the activity can be refreshed to show the new data.
Is this possible? I read a lot of questions here on Stack Overflow on this topic. But in every question, there was another solution. So I want to ask you all: Which solution is the best for my purpose?
Binding the IntentService to the Activity does not seem to be the best solution as there might be conflicts with configuration changes of the activity etc. Correct?
This blog post suggests using AIDL with Parcelables - which sounds very complex to me. There is an easier way, isn't it?
One could set up a broadcast receiver in the activity and fire this broadcast in the IntentService when it is finished.
Some people say you should use createPendingResult() to pass a PendingIntent to the IntentService. If the IntentService finds that PendingIntent in its extras, it uses this to trigger off onActivityResult() in the Activity. Is this the way to choose?
As an example, I use a ResultReceiver to call notifyDataSetChanged() on the adapter of my Activity (which extends ListActivity). It can be adapted to do whatever you need.
ResultReceiver code:
public class MyResultReceiver extends ResultReceiver {
private Context context = null;
protected void setParentContext (Context context) {
this.context = context;
}
public MyResultReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult (int resultCode, Bundle resultData) {
// Code to process resultData here
((BaseAdapter) ((ListActivity)context).getListAdapter()).notifyDataSetChanged();
}
}
MyActivity code:
public class MyActivity extends ListActivity {
private MyResultReceiver theReceiver = null;
...
private void callService () {
theReceiver = new MyResultReceiver(new Handler());
theReceiver.setParentContext(this);
Intent i = new Intent("com.mycompany.ACTION_DO_SOMETHING");
// Code to define and initialize myData here
i.putExtra("someData", myData);
i.putExtra("resReceiver", theReceiver);
startService(i);
}
}
IntentService code:
Bundle resultBundle = new Bundle();
ResultReceiver resRec = intent.getParcelableExtra("resReceiver");
// Do some work then put some stuff in resultBundle here
resRec.send(12345, resultBundle);
When the IntentService completes, it should use LocalBroadcastManager to send an intent to any registered activity.
The IntentService will contain code like this:
private void sendBroadcast() {
Intent intent = new Intent("myBroadcastIntent");
intent.putExtra("someName", someValue);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
The activity receiving the notification will contain code like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String someValue = intent.getStringExtra("someName");
// ... do something ...
}
};
LocalBroadcastManager.getInstance(this)
.registerReceiver(receiver, new IntentFilter("myBroadcastIntent"));
}
For more depth, see the blog post Using LocalBroadcastManager In Service To Activity Communications.
None of the other answers references the official android documentation
https://developer.android.com/training/run-background-service/report-status.html
that states clearly that for the Activity-IntentService communication "The recommended way to send and receive status is to use a LocalBroadcastManager, which limits broadcast Intent objects to components in your own app"!
I would suggest using a Broadcast Receiver in the The Activity waiting for the result.
Your Service would just use sendBroadcast with a custom Intent.
I think the event bus is the way to go. Simple and effective interprocess communication.
http://square.github.io/otto/
https://github.com/greenrobot/EventBus
I've a simple Main Activity which has to stop till an SMS is received... How can I launch a method from the MainActivity within the BroadcastReceiver's onReceive() Method?
Is there away with Signal and Wait? Can I pass something with a pending Intent, or how can I implement this communication?
Communication from BroadcastReceiver to Activity is touchy; what if the activity is already gone?
If I were you I'd set up a new BroadcastReceiver inside the Activity, which would receive a CLOSE message:
private BroadcastReceiver closeReceiver;
// ...
closeReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//EDIT: receiving parameters
String value = getIntent().getStringExtra("name");
//... do something with value
finish();
}
};
registerReceiver(closeReceiver, new IntentFilter(CLOSE_ACTION));
Then from the SMS BroadcastReceiver you can send out this action:
Intent i = new Intent(CLOSE_ACTION);
i.putExtra("name", "value"); //EDIT: this passes a parameter to the receiver
context.sendBroadcast(i);
I hope this helps?
I had the exact same problem, I tried using intent but i was unsuccessful
The easiest way to use it would be using static methods and static variables
MainActivity.java
public static void stopsms()
{
/*
some code to stop the activity
*/
}
SMSReceiver.java
at the end call this function
MainActivity.stopsms();
It works amazing if your code does not affect when you use static methods and variables. Let me know if you need any help.
The problem with registering a second receiver within the activity, however, is that it will not be persistent like registering in the manifest... thus, although, this solution may work, will only work if the activity is running in background.
it's easy, use interface like that:
1) in your broadcast receiver create an interface.
public interface ChangeListener{
public void functionWhoSendData(String type);
public void etc();
}
and instantiate that interface in your broadcast receiver, use it:
public void onReceive(....
String data=functionWhereYouReceiveYouData();
ChangeListener.functionWhoSendData(data);
}
And in your activity, make it implements your interface
When i have a broadcastReceiver say android.intent.action.MEDIA_BUTTON and i want to update the current activity's UI without creating a new activity, is there any good practice on this one?
What i know (might not be correct)
1) I can put the BroadcastReceiver in the same class as the activity and call the updateUI function after certain activity
2) Create a ContentObserver?
3) Communicate to a service created by the activity, use aidl. (I dont know how to get the current service if its registered from an activity)
4) Create a custom filter on the broadcastReceiver located on the same class as the activity, and use context.sendBroadcast(msg of custom filter) and in the custom filter call updateUI (same as one but more generic?)
The final flow is it would come from a BroadcastReceiver and ends up updating the UI without renewing the activity (unless the activity is dead?)
Kindly provide links/source code on your how you tackle this kind of problem. Thanks a lot in advance :)
The easiest way to provide this functionality is to put the broadcast receiver in you Activity and bind / unbind it using registerReceiver and unregisterreceiver:
public class MyActivity extends Activity {
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
MyActivity.this.receivedBroadcast(intent);
}
};
#Override
public void onResume() {
super.onResume();
IntentFilter iff = new IntentFilter();
iff.addAction("android.intent.action.MEDIA_BUTTON");
// Put whatever message you want to receive as the action
this.registerReceiver(this.mBroadcastReceiver,iff);
}
#Override
public void onPause() {
super.onPause();
this.unregisterReceiver(this.mBroadcastReceiver);
}
private void receivedBroadcast(Intent i) {
// Put your receive handling code here
}
}
Depending on the intent you wish to receive, you may need to add the appropriate permissions to your AndroidManifest.xml file.
What I recently had to do to change a Button's text after receiving data from a LocalBroadcastManager is to store the value in a private field and then do the UI stuff in my onResume() method.
public class myClass extends Activity {
private String myString;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// register to receive data
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(receiver, new IntentFilter("myAction"));
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// get the extra data included in the intent
myString = intent.getStringExtra("myString");
}
};
#Override
public void onResume() {
super.onResume();
System.out.println("onResume");
// do something to the UI
myButton.setText(myString != null ? myString : "Default");
}
}