Why / When / How is this Android serviceBinder resetting to null?

Posted by GaZ on Stack Overflow See other posts from Stack Overflow or by GaZ
Published on 2010-03-29T19:36:24Z Indexed on 2010/04/02 12:13 UTC
Read the original article Hit count: 434

Filed under:
|
|
|

I've written a ListActivity for Android 2.1 which is used to display a list of event categories. As the user selects a category, the program calls a web service to retrieve a list of sub-events. For example, a top level event might be "soccer" and when the user selects this the web service would return various soccer associations (e.g. "english", "french", "german", etc.) and display them in a new list.

The following code seems to work occasionally, however sometimes the call to the service (in EventsListTask) fails because the serviceBinder is null. How/Why does this happen?

public class EventListsActivity extends ListActivity {

  private static final String EVENT_ID = "EventId";
  private List<ListItem> eventList;
  private ArrayAdapter<ListItem> listItemArrayAdapter;

  private static final int LOADING_DIALOG = 1;

  private EventsListTask eventsListTask = null;

  private BFService serviceBinder;

  private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
      Log.i("EventListsActivity", "service connected");
      serviceBinder = ((BFService.BFBinder)iBinder).getService();
    }

    public void onServiceDisconnected(ComponentName componentName) {
      Log.i("EventListsActivity", "service disconnected");
      serviceBinder = null;
    }
  };

  @Override
  public void onCreate(Bundle savedInstanceState) {
    Log.i("EventListsActivity", "onCreate");
    super.onCreate(savedInstanceState);
    setContentView(R.layout.list);

    eventList = new ArrayList<ListItem>();
    listItemArrayAdapter = new ArrayAdapter<ListItem>(this, R.layout.row, eventList);
    setListAdapter(listItemArrayAdapter);

    Intent bindIntent = new Intent(this, BFService.class);
    bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);

    int eventId = getIntent().getIntExtra(EVENT_ID, -1);
    if (eventsListTask == null || eventsListTask.getStatus() == AsyncTask.Status.FINISHED) {
      eventsListTask = new EventsListTask();
      eventsListTask.execute(eventId);
    }
  }

  @Override
  protected void onDestroy() {
    Log.i("EventListsActivity", "destroyed");
    super.onDestroy();
    unbindService(mConnection);
  }

  @Override
  protected void onListItemClick(ListView listView, View view, int position, long id) {
    super.onListItemClick(listView, view, position, id);
    ListItem selectedItem = (ListItem) listView.getAdapter().getItem(position);
    Intent intent;
    if (selectedItem.getMarketType() != null) {
      intent = new Intent(this, MarketActivity.class);
      intent.putExtra(EVENT_ID, selectedItem.getId());
      startActivityIfNeeded(intent, -1);
    } else if (selectedItem.getId() != -1) {
      intent = new Intent(this, EventListsActivity.class);
      intent.putExtra(EVENT_ID, selectedItem.getId());
      startActivityIfNeeded(intent, -1);
    } else {
      Log.e("EventListsActivity", "unexpected item selected!");
    }
  }

  @Override
  protected Dialog onCreateDialog(int id) {
    switch (id) {
      case (LOADING_DIALOG) :
        AlertDialog.Builder loadingDialog = new AlertDialog.Builder(this);
        loadingDialog.setTitle("Please Wait...");
        loadingDialog.setMessage("Communicating with remote service.");
        return loadingDialog.create();
    }
    return null;
  }

  private class EventsListTask extends AsyncTask<Integer, Void, LoginStatusEnum> {
    @Override
    protected void onPreExecute() {
      showDialog(LOADING_DIALOG);
    }

    @Override
    protected void onPostExecute(LoginStatusEnum loginStatusEnum) {
      dismissDialog(LOADING_DIALOG);
      if (loginStatusEnum != null) {
        switch (loginStatusEnum) {
          case OK:
            for (ListItem item : eventList) {
              listItemArrayAdapter.add(item);
            }
            listItemArrayAdapter.notifyDataSetChanged();
            break;
        }
      }
    }

    @Override
    protected LoginStatusEnum doInBackground(Integer... params) {
      LoginStatusEnum result = LoginStatusEnum.OK;
      Integer eventId = params[0];
      if (serviceBinder != null) {
        try {
          if (eventId == null || eventId == -1) {
            eventList = serviceBinder.getActiveEventTypes();
          } else {
            eventList = serviceBinder.getEvents(eventId);
          }
        } catch (WebServiceException wse) {
          result = LoginStatusEnum.valueOf(wse.getMessage());
        }
      } else {
        Log.e("EventListsActivity", "serviceBinder is null!");
      }
      return result;
    }
  }
}

EDIT: The serviceBinder appears to be set to null when I reach the bottom of a list, when I change the target intent to go to a different activity:

  intent = new Intent(this, MarketActivity.class);
  intent.putExtra(EVENT_ID, selectedItem.getId());
  startActivity(intent);

This new activity also uses the same background service (binds in the same way, etc.). Is there anything I need to watch out for when doing this? Am I calling the target intent incorrectly?

EDIT2: Here's the output from LogCat when I start the activity which calls the service (this time the service failed straight away!):

04-02 07:02:49.147: INFO/ActivityManager(61): Starting activity: Intent { cmp=net.foobar.activity/.EventListsActivity }
04-02 07:02:49.257: INFO/EventListsActivity(353): onCreate
04-02 07:02:49.426: INFO/EventListsActivity(353): service connected
04-02 07:02:49.437: ERROR/EventListsActivity(353): serviceBinder is null!

© Stack Overflow or respective owner

Related posts about android

Related posts about activity