0

I am creating an Activity which communicates with a Service to download some data from internet via POST method. To do this, I use Messenger. Here is my code to make it clearer for you:

My onCreated() method:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_comments);

    CommentsHandler commentsHandler = new CommentsHandler(this, savedInstanceState);
    Messenger messenger = new Messenger(commentsHandler);

    Intent serviceIntent = new Intent(this, WindowService.class);
    serviceIntent.putExtra("messenger", messenger);
    serviceIntent.putExtra("state", 888);
    serviceIntent.putExtra("number", getIntent().getStringExtra("number"));
    startService(serviceIntent);
}

The code in my Service's thread to post the result data to the Activity via the Messenger object:

    /** ... **/
    Messenger messenger = intent.getParcelableExtra("messenger");
    /** ... **/
    Message resultMsg = this.obtainMessage();
    resultMsg.obj = jParser.getArrayList(); //This is an ArrayList of my downloaded data.
    messenger.send(resultMsg);

The code in the Activity to handle the Message from the Service:

public static class CommentsHandler extends Handler {
    Bundle mSavedInstanceState;
    ActionBarActivity activity;

    public CommentsHandler(Activity a, Bundle savedInstanceState) {
        activity = (ActionBarActivity) a;
        mSavedInstanceState = savedInstanceState;
    }

    @Override
    public void handleMessage(Message msg) {
        comments = (ArrayList<HashMap<String, String>>) msg.obj;
        if (mSavedInstanceState == null && msg.arg1 != 793) {
            activity.getSupportFragmentManager().beginTransaction()
                .add(R.id.container, new CommentsFragment()).commit();
        } else if (msg.arg1 == 793) { //793 is my preferred code to determine
                                      //if the internet connection could not be
                                      //established when the Service was trying
                                      //to download the data.
            activity.finish();
        }
    }
}

The problem is: if I open the Activity and close it before the data is downloaded, this code .add(R.id.container, new CommentsFragment()).commit(); gives me the error Can not perform this action after onSaveInstanceState, because this code only gets executed after the data in my Service is processed and sent via the Messenger object, but at that time the Activity is already closed by the user so the Fragment cannot be added. How to solve this issue? How to check if the Activity is not closed/being closed before adding the Fragment? Or, better, how to stop the thread in which that code is running on Activity's onDestroy() method so it doesn't get executed if the Activity is closed? Thanks in advance!

matiash
  • 54,791
  • 16
  • 125
  • 154
Salivan
  • 1,876
  • 7
  • 26
  • 45

3 Answers3

3

In your activity, you should create a boolean to check if the activity is visible or not:

public ActionBarActivity extends Activity {
  private boolean isActivityVisible = false;

  @Override
  protected void onResume(){
     isActivityVisible = true;
  }

  @Override
  protected void onPause(){
     isActivityVisible = false;
  }

  public boolean isVisible(){
     return this.isActivityVisible;
  }
}

And then you modify your Handler class definition:

public static class CommentsHandler extends Handler {
Bundle mSavedInstanceState;
ActionBarActivity activity;

public CommentsHandler(Activity a, Bundle savedInstanceState) {
    activity = (ActionBarActivity) a;
    mSavedInstanceState = savedInstanceState;
}

@Override
public void handleMessage(Message msg) {
    // here you check if your activity is no longer visible and then break up
    if(activity == null || !activity.isVisible())
        return;

    comments = (ArrayList<HashMap<String, String>>) msg.obj;
    if (mSavedInstanceState == null && msg.arg1 != 793) {
        activity.getSupportFragmentManager().beginTransaction()
            .add(R.id.container, new CommentsFragment()).commit();
    } else if (msg.arg1 == 793) { //793 is my preferred code to determine
                                  //if the internet connection could not be
                                  //established when the Service was trying
                                  //to download the data.
        activity.finish();
    }
}
}
HatemTmi
  • 1,068
  • 9
  • 16
  • Great and very simple solution, but @matiash's answer was more comprehensive with more reasonable explanations. Thank you! – Salivan May 24 '14 at 22:37
1

The smallest change would be to have a boolean field in the Activity, setting it to true in onResume() and to false in onPause(), and check its value in handleMessage() (i.e. ignore the message if the flag is currently false).

Another option, instead of using Messenger and handleMessage(), do this with a BroadcastReceiver. Register the receiver in onResume() and unregister it in onPause(). That way the broadcast from the service will be simply ignored.

Both solutions are basically the same, anyway, but broadcasts are somewhat "higher level".

This assumes that you're not interested in the Service's result if the activity is paused. If you are (for example, if you switch out of the application and back in, and you need to display the update) then you should put the received data in a field and process it on the following onResume().

matiash
  • 54,791
  • 16
  • 125
  • 154
  • Great answer, thank you very much! But if I choose the simplest way (the boolean method you described above), would my code be worse or less appropriate than with the `BroadcastReceiver` implementation? – Salivan May 24 '14 at 22:39
  • 1
    @Salivan Not at all. I just wanted to point out that `LocalBroadcastManager` is very well suited for service->activity communication within an application – matiash May 24 '14 at 22:49
1

Your way of doing this is different than how I would handle it but using what you have I would make these adjustments:

public static class CommentsHandler extends Handler {
    Bundle mSavedInstanceState;
    ActionBarActivity activity;

    public CommentsHandler(Activity a, Bundle savedInstanceState) {
        activity = (ActionBarActivity) a;
        mSavedInstanceState = savedInstanceState;
    }

    public void setActivity(Activity a){
        activity = a;
    }

    @Override
    public void handleMessage(Message msg) {
        if(activity == null){
            return;
        }
        comments = (ArrayList<HashMap<String, String>>) msg.obj;
        if (mSavedInstanceState == null && msg.arg1 != 793) {
            activity.getSupportFragmentManager().beginTransaction()
                .add(R.id.container, new CommentsFragment()).commit();
        } else if (msg.arg1 == 793) { //793 is my preferred code to determine
                                      //if the internet connection could not be
                                      //established when the Service was trying
                                      //to download the data.
            activity.finish();
        }
    }
}

Then I would use your activities onPause()/onResume() methods to like this:

@Override
protected void onPause() {
    super.onPause();
    commentsHandler.setActivity(null);  
}

@Override
protected void onResume() {
    super.onResume();
    commentsHandler.setActivity(this);  
}
Larry McKenzie
  • 3,253
  • 25
  • 22
  • Cool answer! I like your logic pretty much but that's a pitty that you didn't share with me the way you would do it. Thank you anyway! – Salivan May 24 '14 at 22:36
  • Well I do not know the full scope of your project so the way I would do it may change based on that but I would use a library like one mentioned in this post: http://stackoverflow.com/questions/16902716/comparison-of-android-networking-libraries-okhttp-retrofit-volley – Larry McKenzie May 24 '14 at 22:45
  • Hmmm, I don't know the complexity of implementing this library, but I guess that I don't have to use the whole library just to download a couple of JASON strings. Thank you very much anyway. – Salivan May 24 '14 at 22:48