14

When showing a SnackBar from inside an Activity, I have the rootView available. But I need to show a SnackBar from inside a Service, where I do not have a View available. How might I accomplish this?

As backstory: an activity starts a service to do a task. The Service needs to show a SnackBar depending on situations. I don’t want to Bind to the Service just for that. So how might I accomplish this? Normally I could show a Toast, but I need the user to be able to read the message and confirm so.

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Nouvel Travay
  • 6,292
  • 13
  • 40
  • 65
  • To clarify, are you asking about being able to show the Snackbar while the user is using the app, or anytime, even if the app is closed? – Phil Jan 18 '16 at 20:24

4 Answers4

17

The Snackbar needs a View to be displayed, so if you want to show snackbars on your app depending on the state of your Service you'll have to either bind it to your Activity or broadcast a message through the LocalBroadcastManager and show a message on your View.

I don't think there's any other way around it, you'll have to communicate with your Activity or Fragment somehow.

Snackbars are not like Toasts that only need a context, so if you want to display it out of your app, I believe you can't with the class provided by Android.

As from the design guidelines:

Placement

Snackbars appear above most elements on screen, and they are equal in elevation to the floating action button. However, they are lower in elevation than dialogs, bottom sheets, and navigation drawers.

It's not explicit, but you can reach the conclusion that it'll only display inside your app views. So, again, you'll have to communicate with your visible view somehow.


Snippets on broadcasting a message:

Sender (on your Service)

private void doSendBroadcast(String message) {
    Intent it = new Intent("EVENT_SNACKBAR");

    if (!TextUtils.isEmpty(message))
        it.putExtra(EXTRA_RETURN_MESSAGE,message);

    LocalBroadcastManager.getInstance(mContext).sendBroadcast(it);
}

Receiver (on your Activity)

private BroadcastReceiver mMessageReceiver = null;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Other stuff.

    mMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Do something
        }
    };
}

@Override
public void onResume() {
    super.onResume();

    LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter("EVENT_SNACKBAR"));
}

More on bound services here.

And about LocalBroadcastManager here on this question.

Update: You could also make use of an EventBus to communicate with your visible view, as it works on a Publisher/Subscriber fashion. You could even make use of the concept of Sticky events to make sure the Snackbar will be displayed once the app is visible again.

Take a look at this answer of mine on how to use the Event Bus.

Mauker
  • 11,237
  • 7
  • 58
  • 76
5

You could always send a broadcast through the LocalBroadcastManager in your Service

LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("SERVICE_DID_SOMETHING"));

And then in the Activity use a BroadcastReceiver to show the Snackbar

private final ServiceReceiver _serviceReceiver = new ServiceReceiver();

@Override
protected void onResume() {
    super.onResume();

    final IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("SERVICE_DID_SOMETHING");

    LocalBroadcastManager.getInstance(this).registerReceiver(_serviceReceiver, intentFilter);
}

@Override
protected void onPause() {
    super.onPause();

    try {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(_serviceReceiver);
    } catch (Exception ex) {
        Log.e(TAG, "Error unregistering ServiceReceiver", ex);
    }
}

// region ServiceReceiver
private class ServiceReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: Show your Snackbar here...
    }
}
// endregion

You can use the Intent you send to pass additional data if needed and then pull it out in the BroadcastReceiver.onReceive(Context context, Intent intent)

Cory Charlton
  • 8,868
  • 4
  • 48
  • 68
1

How to create a Snackbar with the application context which is visible across multiple activities: https://stackoverflow.com/a/37707741/1185087

Community
  • 1
  • 1
user1185087
  • 4,468
  • 1
  • 30
  • 38
-1

It is not possible to do it with the default SnackBar provided by the SDK, since it will always require a view.

To serve your purpose you can use some external library which mimics a SnackBar like this one

Viral Patel
  • 32,418
  • 18
  • 82
  • 110
  • 3
    would appreciate a reason when someone downvotes unless it is by competing answers seeking attention. :) – Viral Patel Jan 18 '16 at 20:13
  • 2
    That tends to happen a lot in here. I hate it, it's not constructive. – Mauker Jan 18 '16 at 20:20
  • 1
    No, @AndroidMechanic, you must have to pass a view to SnackBar provided with the SDK or this library. You will be able to find 2 constructors. #1 `new SnackBar.Builder(this)` - `this` as activity. #1 `new SnackBar.Builder(getActivity().getApplicationContext(), root)` - `root` as view root –  Sep 24 '17 at 13:15