23

I'm using EventBus to communicate between Activity and Service. Today I got a problem and don't know why.

  1. I have Activity, Fragment and Service. All of them are working fine.

  2. In Activity and Fragment I registered them to Receive events which delivered from Service

  3. In Activity and Fragment, I un-register them when onDestroy() was called.

  4. In normal cases, when Services delivers events, Fragment and Activity can receive those events and work well.

  5. But when App is pushed on the background (by presses Home or Power button), only Fragment receives events which delivered from Service, and Activity does not receive them.

  6. I did not do anything in onPause() both of Activity and Fragment.

Question:

Is there any explanation for that? And how can I make my Activity receives event like Fragment did when app is pushed on background?

ThaiPD
  • 3,503
  • 3
  • 30
  • 48
  • Maybe activity's `onDestroy` gets called and it unregisters it. – Vucko Mar 16 '16 at 10:07
  • Remove `unregister(this);` from your onDestroy method, and try that. – Vucko Mar 16 '16 at 10:08
  • `I'm using EventBus to communicate between Activity and Service` this is a wrong approach, use "bound local service" pattern instead – pskink Mar 16 '16 at 10:09
  • Can you explain why it's wrong approach @pskink – ThaiPD Mar 16 '16 at 10:10
  • 2
    because of problems you have now? if you press home / back button your activity can be destroyed at any time, so it will not receive any data – pskink Mar 16 '16 at 10:12
  • 1
    I don't think its fair to completely dismiss eventbus as an option for app/service communication. It just depends on the purpose of the messages. If the app depends on the service's state, then binding is the best option. If the service raises events, then a bus is better. Also, sticky events will let your activity receive events once it is registered again. – Kevin Mar 16 '16 at 10:15
  • @Vucko: I tried and no affect. Also I believe that onDestroy was not called in this case – ThaiPD Mar 16 '16 at 10:15
  • @pskink: it does not explain why Activity did not receive event while Fragment did. I agree with you that activity can be destroyed when app is in background but that time Fragment also be destroyed. – ThaiPD Mar 16 '16 at 10:21
  • @Kevin: stickyEvent still not work in this case :( – ThaiPD Mar 16 '16 at 10:52
  • so this is a potential bug in Fragments - they should behave as Activities – pskink Mar 16 '16 at 11:08
  • I don't think its a bug. Based on these posts [link1](http://stackoverflow.com/questions/21668531/is-ondestroy-guaranteed-to-be-called-for-fragments) and [link2](http://stackoverflow.com/questions/17195641/fragment-lifecycle-when-ondestroy-and-ondestroyview-are-not-called) `onDestroy` is not always going to be called for fragments. I think register/unregister should be in onStart/onStop; not onCreate/onDestroy – Kevin Mar 16 '16 at 11:23
  • @Kevin both of onDestroy() in fragment and activity was not called in here, I'm sure. – ThaiPD Mar 16 '16 at 11:28
  • need to see some sample code – Naveen Prince P Mar 21 '16 at 13:25
  • Can you add a log in onDestroy to see if it is triggered? – tiny sunlight Mar 25 '16 at 12:58

5 Answers5

18

In EventBus version 3.0.0 you can use Sticky posts.

This way you can and should unregister EventBus on 'onStop()' to prevent memory leaks and still receive events when App come to foreground.

Post events this way:

EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));

Subscribe events with sticky flag, like this:

@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {   
    textField.setText(event.message);
}

EventBus Documentation: http://greenrobot.org/eventbus/documentation/configuration/sticky-events/

Kunami
  • 237
  • 3
  • 8
14

When user presses back/home button, Activity can be destroyed anytime and thus you won't be able to receive the data using EventBus. If any how you are trying to receive the data when the Activity is in background, it may leak memory and the app will crash.

There can be other approaches to get the data in the Activity when user resumes the activity.

You can either user sharedpreferences or local database to save the results passed be the service. And when the user navigates back to the activity, read it from sharedpreferences or database.

This way there won't be any issue with memory leakage or data loss.

Edit 1:

It is always recommended to unregister listeners in either onPause or onStop because the activity does not need those events when it is not in the foreground. And since onDestroy() is not guaranteed to be called, thus you could continue receiving broadcasts when the Activity is no longer open.

Rohit Arya
  • 6,751
  • 1
  • 26
  • 40
  • I'd like to ask why I can not receive event in Activity while Fragment did? when Activity was destroyed I had unregister to receive event and I dont need to receive event any more. By my question is why onDestroyed() was not called yet but I still can not receive event. – ThaiPD Mar 22 '16 at 09:27
  • what is the last callback till which you are able to receive the event? `onPause` or `onStop`? – Rohit Arya Mar 22 '16 at 09:31
  • yes, problem just occurs when App is pushed on background. But Fragment work well – ThaiPD Mar 22 '16 at 09:53
  • Ok, so let me clarify first. You are able to receive events before `onStop` is called. And you since you are unregistering for events in `onDestroy`, you are expecting events till `onDestroy` is called. Right? – Rohit Arya Mar 22 '16 at 09:57
  • What if the `activity` is getting killed right after `onStop` but `onDestroy` is not being called. And thus you are unable to receive the events? – Rohit Arya Mar 22 '16 at 12:20
  • I' not agree with you in this point. Let's talk about normal case, when I just press home button and then event is dispatch, just few sec and that time activity was not destroyed. you can see in my question that at the same time Fragment is receive event and activity is not. That mean all of them still alive here – ThaiPD Mar 24 '16 at 07:06
  • okay, when `onStop` has already been called, how are you checking if events are being received by the `activity`? – Rohit Arya Mar 24 '16 at 07:46
  • This is my answer, but just rephrased. You can see the dates – M. Reza Nasirloo Mar 26 '16 at 07:59
  • Sometimes I need to unregister the bus inside the `onDestroy()` method to ensure that the `Activity` or `Fragment` can receive posted events. – blueware Oct 04 '17 at 08:20
3

The Activity class provides two lifecycle methods, onStop() and onRestart() when is not visible (background mode), which allow you to specifically handle how your activity handles being stopped and restarted. Unlike the paused state, which identifies a partial UI obstruction, the stopped state guarantees that the UI is no longer visible and the user's focus is in a separate activity (or an entirely separate app).

In order to understand this cycle, take a look a this image that shows the flow when your app goes out of foreground mode.

When the user leaves your activity

In your case you can handle the issue like this.

  • Provide to the user a mechanism to save persistent application data using either local database(Sqlite), sharedPreferences.
  • Handle your data persistency on onStop() method.
  • When user callback your app, then you will need to restore the data using onRestart() method.

    Here is the way to implement that.

    public class Calc extends Activity {
    public static final String PREFS_NAME = "MyPrefsFile";
    
    @Override
    protected void onCreate(Bundle state){
       super.onCreate(state);
       . . .
    
       // Restore preferences
       SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
       boolean silent = settings.getBoolean("silentMode", false);
       setSilent(silent);
    }
    
    @Override
    protected void onStop(){
       super.onStop();
    
      // We need an Editor object to make preference changes.
      // All objects are from android.context.Context
      SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
      SharedPreferences.Editor editor = settings.edit();
      editor.putBoolean("silentMode", mSilentMode);
    
      // Commit the edits!
      editor.commit();
    }
    

    }

Please read the following documentation from Android Developer site

Carlos
  • 963
  • 9
  • 19
2

It's hard to guess what you have done which causing this behavior, consider providing some code.

But what is obvious is that you have some design flaws.

You must unregister from any event bus or listener in your ui components like Activities or Fragments when user navigates back from the app, if you don't, there is a good chance to leak your activity and all of the resources which it holds.

You should store any data which you receive or calculate in your background service to a file or database, when user open or reopen your app you should check for that data and act on it.

M. Reza Nasirloo
  • 16,434
  • 2
  • 29
  • 41
2

Needs more codes or examples to properly help you. But try the following.

  1. Are your activities extended from a baseActivity? and if so remove the onDestroy eventbus unregister code and check.
  2. In developer options, check whether the don't keep activities option is unchecked.
  3. Pressing back will kill your app anyway unless u have overridden the back event.
Kishath
  • 5,560
  • 4
  • 17
  • 22