title: Android - Thread within an activity allowed to call member functions of a bound service?
body: <p>I have a service bound to an activity. The service will access a JSON web service. I would like to create a thread within the activity that calls a member function of the bound service but I am receiving an error. </p>
  
  <p>The following example functions perfectly if I don't use a thread. </p>
  
  <p>Here is the activity:</p>
  
  <pre><code>import java.util.ArrayList;
  
  import android.app.ListActivity;
  import android.app.ProgressDialog;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.ServiceConnection;
  import android.os.Bundle;
  import android.os.IBinder;
  import android.util.Log;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.AdapterView;
  import android.widget.AdapterView.OnItemClickListener;
  import android.widget.ArrayAdapter;
  import android.widget.ListView;
  import android.widget.TextView;
  import android.widget.Toast;
  
  
  public class ProjectsListActivity extends ListActivity {
  
      ApiService mService;
      boolean mBound = false;
  
      private static String TAG = "tag";
      private ProgressDialog mProgressDialog = null;
      private ArrayList&lt;Project&gt; mProjects = null;
      private ProjectAdapter mAdapter;
      private Runnable viewProjects;
  
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          // set the layout
          setContentView(R.layout.project_list_item);
  
          // setup the custom list adapter
          mProjects = new ArrayList&lt;Project&gt;();
          this.mAdapter = new ProjectAdapter(this, R.layout.row, mProjects);
          setListAdapter(this.mAdapter);
  
          ListView lv = getListView();
          lv.setTextFilterEnabled(true);
  
          lv.setOnItemClickListener(new OnItemClickListener() {
              public void onItemClick(AdapterView&lt;?&gt; parent, View view,
                      int position, long id) {
  
                  if (mBound) {
                      Project p = mProjects.get(position);
                      Toast.makeText(getApplicationContext(), p.getName(),
                              Toast.LENGTH_SHORT).show();
                  }
              }
          });
  
          // use a thread to get the projects
          viewProjects = new Runnable() {
              @Override
              public void run() {
                  getProjects();
              }
          };
          Thread thread = new Thread(null, viewProjects, "MagentoBackground");
          thread.start();
          mProgressDialog = ProgressDialog.show(ProjectsListActivity.this,
                  "Please wait...", "Retrieving data ...", true);
  
      }
  
      @Override
      protected void onStart() {
          super.onStart();
          // Bind to LocalService
          Intent intent = new Intent(this, ApiService.class);
          bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
      }
  
      @Override
      protected void onStop() {
          super.onStop();
          // Unbind from the service
          if (mBound) {
              unbindService(mConnection);
              mBound = false;
          }
      }
  
      /** Defines callbacks for service binding, passed to bindService() */
      private ServiceConnection mConnection = new ServiceConnection() {
  
          @Override
          public void onServiceConnected(ComponentName className, IBinder service) {
              mService = ((ApiService.LocalBinder)service).getService();
              mBound = true;
          }
  
          @Override
          public void onServiceDisconnected(ComponentName arg0) {
              mBound = false;
          }
      };
  
      private class ProjectAdapter extends ArrayAdapter&lt;Project&gt; {
  
          private ArrayList&lt;Project&gt; items;
  
          public ProjectAdapter(Context context, int textViewResourceId,
                  ArrayList&lt;Project&gt; items) {
              super(context, textViewResourceId, items);
              this.items = items;
          }
  
          @Override
          public View getView(int position, View convertView, ViewGroup parent) {
              View v = convertView;
              if (v == null) {
                  LayoutInflater vi = (LayoutInflater) getContext()
                          .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                  v = vi.inflate(R.layout.row, parent, false);
              }
              Project o = items.get(position);
              if (o != null) {
                  TextView tt = (TextView) v.findViewById(R.id.toptext);
                  TextView bt = (TextView) v.findViewById(R.id.bottomtext);
                  if (tt != null) {
                      tt.setText("Name: " + o.getName());
                  }
                  if (bt != null) {
                      bt.setText("Status: " + o.getId());
                  }
              }
              return v;
          }
      }
  
      private Runnable returnRes = new Runnable() {
          @Override
          public void run() {
              if (mProjects != null &amp;&amp; mProjects.size() &gt; 0) {
                  mAdapter.notifyDataSetChanged();
                  for (int i = 0; i &lt; mProjects.size(); i++)
                      mAdapter.add(mProjects.get(i));
              }
              mProgressDialog.dismiss();
              mAdapter.notifyDataSetChanged();
          }
      };
  
      private void getProjects() {
          try {
              mProjects = mService.getUserProjects();
              Log.i(TAG, "" + mProjects.size());
          } catch (Exception e) {
              Log.e("BACKGROUND_PROC", "Exception ["+e.getMessage()+"]", e);
          }
          runOnUiThread(returnRes);
      }
  
  }
  </code></pre>
  
  <p>Here is the service:</p>
  
  <pre><code>import java.util.ArrayList;
  
  import android.app.Service;
  import android.content.Intent;
  import android.os.Binder;
  import android.os.IBinder;
  
  
  public class ApiService extends Service {
  
      // Binder given to clients
      private final IBinder mBinder = new LocalBinder();
  
      /**
       * Class used for the client Binder. Because we know this service always
       * runs in the same process as its clients, we don't need to deal with IPC.
       */
      public class LocalBinder extends Binder {
          ApiService getService() {
              return ApiService.this;
          }
      }
  
      @Override
      public IBinder onBind(Intent intent) {
          return mBinder;
      }
  
      public ArrayList&lt;Project&gt; getUserProjects() {
  
          ArrayList&lt;Project&gt; mProjects = new ArrayList&lt;Project&gt;();
          Project p1 = new Project();
          p1.setName("Project 1");
          p1.setId("Pending");
          Project p2 = new Project();
          p2.setName("Project 2");
          p2.setId("Completed");
          mProjects.add(p1);
          mProjects.add(p2);
  
          return mProjects;
      }
  }
  </code></pre>
  
  <p>The error I receive:</p>
  
  <pre><code>06-10 18:45:32.736: ERROR/BACKGROUND_PROC(285): Exception [null]
  06-10 18:45:32.736: ERROR/BACKGROUND_PROC(285): java.lang.NullPointerException
  06-10 18:45:32.736: ERROR/BACKGROUND_PROC(285):     at ProjectsListActivity.getProjects(ProjectsListActivity.java:160)
  06-10 18:45:32.736: ERROR/BACKGROUND_PROC(285):     at ProjectsListActivity.access$3(ProjectsListActivity.java:158)
  06-10 18:45:32.736: ERROR/BACKGROUND_PROC(285):     at ProjectsListActivity$4.run(ProjectsListActivity.java:67)
  06-10 18:45:32.736: ERROR/BACKGROUND_PROC(285):     at java.lang.Thread.run(Thread.java:1096)
  </code></pre>
  
  <p>If this still doesn't make sense, essentially what I am trying to do is take the <a href="http://developer.android.com/guide/topics/fundamentals/bound-services.html" rel="nofollow" title="bound service">bound service</a> example with the number generator and modify it so that the method is called within a thread in the activity. Within the example it even says, "However, if this call were something that might hang, then this request should occur in a separate thread to avoid slowing down the activity performance." </p>
  
  <p>Thanks in advance for your help. </p>