I'm currently trying to get broadcast receivers running in the background of my android application, which I've been told to use an event service for. At present my broadcast receivers work fine if you're in the activity within which they're registered
private BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_BATTERY_LOW)) {
Toast.makeText(arg0, "Battery's dying!!", Toast.LENGTH_LONG).show();
Log.e("LOW", "LOW");
intent = null;
}else if(intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)) {
Toast.makeText(arg0, "Battery's discharging!!", Toast.LENGTH_LONG).show();
Log.e("discharge", "discharge");
intent = null;
}else if(intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) {
Toast.makeText(arg0, "Battery's charging!!", Toast.LENGTH_LONG).show();
Log.e("charge", "charge");
intent = null;
}else if(intent.getAction().equals(Intent.ACTION_BATTERY_OKAY)) {
Toast.makeText(arg0, "Battery's okay!!", Toast.LENGTH_LONG).show();
Log.e("OKAY", "OKAY");
intent = null;
}
}
};
in OnCreate:
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_BATTERY_LOW));
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_BATTERY_OKAY));
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_POWER_DISCONNECTED));
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_POWER_CONNECTED));
Despite the issues that arise with leaking intentfilters by making them in oncreate, my current issue that that when I change activity these no longer run, I've read from the following: http://www.vogella.com/tutorials/AndroidBroadcastReceiver/article.html#startingservices_alarmmanager
That by placing these into an IntentService and starting this service, they'll run consistently, however this involves registering the receivers in the manifest, which gives me issues in that my application is planning on allowing the user to listen out for specific events IE: these cannot be created dynamically.
Is there a way to dynamically create broadcast receivers within a class, which runs in the background of the application and is triggered when the broadcast occurs?
Related
I'm trying to create a pinned shortcut on the desktop for an app. The CreateShortcut method is called from a button and presents the android create-shortcut dialog. When the caller selects ok, the broadcast receiver should get called and execute finish so the activity exits.
This is the first time I've used a broadcast receiver but it looks like it's pretty straight-forward. Just create a receiver, register it with an intent filter that has the same action as an intent and when the intent is sent it should cause the receiver to be called, right?
The shortcut is created just fine but the broadcast receiver never gets called. I'm not seeing any messages on logcat.
private void CreateShortcut(final Context c) {
if (ShortcutManagerCompat
.isRequestPinShortcutSupported(c)) {
Intent shortcutIntent = new Intent(
c, CreateAppHomeShortcut.class);
shortcutIntent.setAction(
Intent.ACTION_CREATE_SHORTCUT);
ShortcutInfoCompat shortcutInfo
= new ShortcutInfoCompat
.Builder(c, "shortcut")
.setShortLabel(c.getString(R.string.app_name))
.setIcon(IconCompat.createWithResource(
c, R.drawable.qmark)
)
.setIntent(shortcutIntent)
.build();
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(
Context context, Intent intent) {
Log.d(TAG, "msg received");
unregisterReceiver(this);
finish();
}
}
, new IntentFilter(
Intent.ACTION_CREATE_SHORTCUT
)
);
PendingIntent successCallback =
PendingIntent.getBroadcast(
c, 99
, shortcutIntent, 0
);
ShortcutManagerCompat.requestPinShortcut(c,
shortcutInfo,
successCallback.getIntentSender()
);
}
}
I've been working on this several days and I'm stumped.
Thanks
I finally got the callback to my BroadcastReceiver. My main problem was that I was using the intents wrong. I thought that the brodcast receiver intent and the shortcut intent could be the same as long as the action was correct. Wrong! The shortcut intent must hava an action set but in the tests I did, it didn't seem to care what that action was. And the broadcast receiver was created as "Intent = new Intent(context, class); setAction(...); ", the shortcut would be created and function fine but the broadcast receiver was never invoked. The only way I could get the broadcast receiver to work was with an Intent just for it with nothing but the action set (or possibly extras) set. I couldn't get the program to work using the same intent to create the shortcut and invoke the broadcast receiver.
The other problem encountered was that the interface allows you to create multiple pinned shortcuts -- and would then call your broadcast receiver once for each shortcut created. I discovered you can query the interface for all pinned shortcuts and filter by id to find out if your shortcut already exists and use that info to avoid creating multiple identical pinned shortcuts on your home page.
The code below seems to work fine API26+ for creating a shortcut and the receiver gets called as long as the user accepts the shortcut. The docs state that they will only call your receiver on the user's acceptance. That of course makes detecting the end of the user's interaction rather difficult. Since the request gets buried in my actual app, the plan was to open this as part of a separate activity, but I don't have any way to detect that the user is done if he doesn't want the shortcut. If anyone has suggestions, I'd appreciate hearing them.
// Create a shortcut and exit the activity. If the shortcut
// already exists,just exit.
private void CreateShortcut(final Context c) {
if (Build.VERSION.SDK_INT >= 26) {
ShortcutManager sm =
getSystemService(ShortcutManager.class);
if (sm != null && sm.isRequestPinShortcutSupported()) {
final String shortcutId = "StartApp";
boolean shortcutExists = false;
// We create the shortcut multiple times if given the
// opportunity. If the shortcut exists, put up
// a toast message and exit.
List<ShortcutInfo> shortcuts
= sm.getPinnedShortcuts();
for (int i = 0;
i < shortcuts.size() && !shortcutExists; i++) {
shortcutExists
= shortcuts.get(i).getId().equals(shortcutId);
if (shortcutExists) {
Toast.makeText(c , String.format(
"Shortcut %s already exists."
, shortcutId
)
, Toast.LENGTH_LONG
).show();
finishActivity();
}
else {
// this is the intent that actually creates the
// shortcut.
Intent shortcutIntent
= new Intent(c, CreateAppHomeShortcut.class);
shortcutIntent.setAction(
Intent.ACTION_CREATE_SHORTCUT);
ShortcutInfo shortcutInfo = new ShortcutInfo
.Builder(c, shortcutId)
.setShortLabel(
c.getString(R.string.app_name))
.setIcon(createWithResource(c
, R.drawable.qmark))
.setIntent(shortcutIntent)
.build();
// this intent is used to wake up the broadcast
// receiver.
// I couldn't get createShortcutResultIntent to
// work but just a simple intent as used for a
// normal broadcast intent works fine.
Intent broadcastIntent
= new Intent(Intent.ACTION_CREATE_SHORTCUT);
// create an anonymous broadcaster. Unregister
// to prevent leaks when done.
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(
Context c, Intent intent) {
unregisterReceiver(this);
Log.d(TAG, String.format(
"ShortcutReceiver activity = "
+ "\"$1%s\""
, intent.getAction()));
finishActivity();
}
}
, new IntentFilter(
Intent.ACTION_CREATE_SHORTCUT)
);
PendingIntent successCallback
= PendingIntent.getBroadcast(
c, 99
, broadcastIntent, 0);
// Shortcut gets created here.
sm.requestPinShortcut(shortcutInfo
, successCallback.getIntentSender());
}
}
}
}
I've look at many solutions to other questions with similar issues but I can't figure out what's wrong with my code. I understand that LocalBroadcast is a popular way to do this and I've spent time trying to implement it. At the moment, the receiver isn't declared in my manifest but from what I understand, that's what the register lines are for.
In my activity:
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("MyActivity", "onReceive");
String action = intent.getAction();
int current = intent.getIntExtra("test", 0);
Toast.makeText(MyActivity.this, current.toString(), Toast.LENGTH_LONG).show();
}
};
#Override
public void onResume() {
super.onResume();
Log.d("MyActivity", "onResume()");
LocalBroadcastManager.getInstance(MyActivity.this).registerReceiver(
mMessageReceiver, new IntentFilter("currentUpdate"));
}
#Override
protected void onPause() {
Log.d("MyActivity", "onPause()");
LocalBroadcastManager.getInstance(MyActivity.this).unregisterReceiver(mMessageReceiver);
super.onPause();
}
In the service I have a method defined:
private void sendNewBroadcast(Intent intent, int current){
intent.putExtra("test", current);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Log.d("MyService", "new Broadcast sent from service");
}
and I use it like this elsewhere in the service:
Intent intent = new Intent("currentUpdate");
sendNewBroadcast(intent, 5);
I've debugged and everything seems to be working except for the 'receiving' part. Am I missing something? The service is started in a different activity and is ongoing.
Firstly, the action String on the broadcast Intent needs to match the action set on the IntentFilter you're registering the Receiver with. Originally, they were different, but it was possibly just a typo.
Secondly, LocalBroadcastManager does not work across processes. The Activity and the Service must be running in the same process to be able to use LocalBroadcastManager. If the Service needs to be in a separate process, you'll have to use some other mechanism; e.g., Intents, broadcasts sent and received on a Context, some event bus implementation that supports IPC, etc.
I have an android service that I need to stop and restart. In the service's onDestroy(), I have some clean up code that may take a while to execute.
Is there away to notify the application that's using the service that it's finish executing onDestroy()? Or something the application can do to check if onDestroy() has finish its execution?
Yes, use broadcast in onDestroy() method!
Intent intent = new Intent( "Service_destroyed" );
// You can also include some extra data.
intent.putExtra("message", "Service is destroyed!");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
That will notify the other activities. Be sure to register the broadcast first in the activities and unregister when not needed anymore.
Register:
LocalBroadcastManager.getInstance(this).registerReceiver(mServiceDestroyedReceiver, new IntentFilter( "Service_destroyed" ));
Here is the method to catch the broadcast:
private BroadcastReceiver mServiceDestroyedReceiver= new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Get extra data included in the Intent
String message = intent.getStringExtra("message");
Log.d(TAG, "Service is destroyed message: " + message);
}
};
Unregister broadcast receiver:
LocalBroadcastManager.getInstance(this).unregisterReceiver( mServiceDestroyedReceiver);
Which is better? To instantiate a Broadcast receiver inside an Activity or in Service class, or make a class that extends BroadcastReceiver?
Below is an example where I instantiate BroadcastReceiver inside a Service class.
 public BroadcastReceiver receiver = new BroadcastReceiver() {
private String filename;
#Override
public void onReceive(Context context, Intent intent){
String action = intent.getAction();
Bundle extras = intent.getExtras();
filename = extras.getString("AudioPath");
Toast.makeText(AudioService.this, "the audio file name sent: " + filename , Toast.LENGTH_LONG).show();
if(action.equals("com.porno.xxx.AudioPlay")){
selectedAudioPath = audiopath;
String state = intent.getExtras().getString("stringdata");
playSong();
Toast.makeText(AudioService.this, "play audio from service string data "+ state, Toast.LENGTH_LONG).show();
}
else if(action.equals("com.porno.xxx.AudioPause")){
pauseSong();
selectedAudioPath = audiopath;
Toast.makeText(AudioService.this, "pause audio from service", Toast.LENGTH_LONG).show();
}
else if(action.equals("com.porno.xxx.AudioSelector")){
Toast.makeText(AudioService.this, "music selector from service", Toast.LENGTH_LONG).show();
Intent i = new Intent();
audiopath = intent.getStringExtra("filename");
Toast.makeText(AudioService.this, "selelcted audio path: " + audiopath, Toast.LENGTH_LONG).show();
}
else if(action.equals("com.porno.xxx.AudioRelease")){
Toast.makeText(AudioService.this, "My Service Stopped and destoryed", Toast.LENGTH_LONG).show();
player.stop();
if (player != null) player.release();
}
}
};
First you plan what you want to do with broadcast receiver. Then you analyze the best and feasible solutions.
If you want to register and unregister the broadcast receiver inside the activity then your source code is ok.
For example if you want to invoke the broadcast receiver when the application is not executing.(which means come out of application and not force close). Then you should not register and unregister the broadcast receiver in coding.
For that you create/implement a seperate class extends from BroadcastReceiver.
In manifest file you want to add the broadcast receiver.
An answer was submitted and accepted while I was typing, so here's where I was at, glad you found your answer already! :)
Based on your (apparent) use as a media player, I'd recommend implementing the player as a Service (that can continue to run in the background if the user navigates away) or as an Activity (if this functionality isn't desired or appropriate for your app..)
A typical implementation of a BroadcastReceiver is as a stand-alone component of the application, declared in the Manifest, which allows it to receive broadcasts even when the application has been killed; it would be started to receive the broadcast, and then stopped after processing it.
Instead it might be advantageous to create the BroadcastReceiver as an inner class, as you've done. This is great when you're only handling your own actions, as it is easy to start and stop the receiver.
In your Service's onCreate() method you can create the action filter, then it can easily be enabled or disabled based on the state of your application:
/* service */
public static final ACTION_PAUSE = "com.example.action_pause";
#Override
public void onCreate() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON); /* Android action example */
filter.addAction(MyClass.ACTION_PAUSE); /* Custom action example*/
registerReceiver(mIntentReceiver, filter);
}
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.v(TAG, "mIntentReceiver.onReceive() action:"+ intent.getAction() );
handleCommand(intent);
} // end onReceive
}; /* end BroadcastReceiver */
Good day, I have an activity which i navigate to from an icon on an appwidget using pending Intents. Everything is being done in a service class. Now, the activity has a refresh button which when pressed, it sends an intent that calls the onStart() method on the service to update itself and perform some web operations. How do i go about reflecting the changes that could have occurred from the service in the activity without temporarily existing the activity.
Service to Activity:
if(intent.getExtras()!= null){
appWidgetId = intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
//if i get this action from my detailedinfo class add a boolean to it
if(intent.getAction() == refresh_action){
// boolean variable to hold condition
my_action = true;
}
Intent forecast = new Intent(this,detailedInfo.class );
forecast.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
forecast.putExtra("cityname", city);
PendingIntent forecastIntent = PendingIntent.getActivity(this, 0, forecast, 0);
/*onclick to go to detailedInfo class*/
remoteView.setOnClickPendingIntent(R.id.city_image_id, forecastIntent);
if(my_action == true){
//Log.d(TAG, "my_action is true, performing pending intent");
try {
forecastIntent.send(this, 0, forecast);
} catch (CanceledException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
And in the Activity class:
Intent service = new Intent(this, cityService.class);
service.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
service.setAction(refresh_action);
Uri data = Uri.withAppendedPath(Uri.parse(CityWidgetProvider.URI_SCHEME + "://widget/id/"), String.valueOf(appWidgetId));
service.setData(data);
startService(service);
I tried adding a setAction() method to the intent that calls the service and then use the same pendingIntent(even though i think is a long shot) but they seems to be ignored. Please how do i go about this and what could i have been doing wrong.? As usual any help is highly appreciated. Thank you.
I'm not 100% clear on what you're trying to do, but the easiest thing to do would be to register a BroadcastReceiver in your Activity onResume (remove it in onPause). When the service is done with whatever it needs to do, broadcast that info.
In the Activity
public static final String ACTION_STRING = "THE_BIG_ACTION";
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do whatever you want here
Toast.makeText(getApplicationContext(), "received", Toast.LENGTH_SHORT);
}
};
#Override
protected void onResume() {
super.onResume();
registerReceiver(receiver, new IntentFilter(ACTION_STRING));
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
In the service, when you're done, just call...
sendBroadcast(new Intent(YourActivityClass.ACTION_STRING));
If you want to include some data, just put it in the intent like you would when starting an Activity.
If your Activity is off screen when the service completes, and the user goes back to it, you'll have missed the notification. That's a different issue to resolve.