title: Android sending messages between fragment and service
body: <p>I have a fragment with a button. When clicked it tells a service to start polling sensors and then insert the sensor data into a database on a background thread. When the button is pushed again, the service will stop. When the Stop button is pushed, there may still be tasks in the executor queue that is inserting into the DB, so during this time I want to display a progress dialog, and dismiss it once the entire queue is clear. The fragment with the button looks like this:</p>
  
  <pre><code>public class StartFragment extends Fragment implements View.OnClickListener {
  
      Button startButton;
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container,
                               Bundle savedInstanceState) {
  
          View view = inflater.inflate(R.layout.fragment_start, container, false);
  
          startButton = (Button) view.findViewById(R.id.startButton);
          startButton.setOnClickListener(this);
  
          return view;
      }
  
      @Override
      public void onClick(View v) {
          if (recording has not yet started){ 
              mainActivity.startService(new Intent(mainActivity, SensorService.class));
          } else {
              //I want to display a progress dialog here when the service is told to stop
              //Once all executor task queue is clear, I want to dismiss this dialog
              mainActivity.stopService(new Intent(mainActivity, SensorService.class));
          }
      }
  }
  </code></pre>
  
  <p>When the button is clicked the first time, the following service will start:</p>
  
  <pre><code>public class SensorService extends Service implements SensorEventListener {
  
      public static final int SCREEN_OFF_RECEIVER_DELAY = 100;
  
      private SensorManager sensorManager = null;
      private WakeLock wakeLock = null;
      ExecutorService executor;
      Runnable insertHandler;
  
      private void registerListener() {
          //register 4 sensor listeners (acceleration, gyro, magnetic, gravity)
      }
  
      private void unregisterListener() {
          sensorManager.unregisterListener(this);
      }
  
      public BroadcastReceiver receiver = new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
              Log.i(TAG, "onReceive("+intent+")");
  
              if (!intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                  return;
              }
  
              Runnable runnable = new Runnable() {
                  public void run() {
                      Log.i(TAG, "Runnable executing...");
                      unregisterListener();
                      registerListener();
                  }
              };
  
              new Handler().postDelayed(runnable, SCREEN_OFF_RECEIVER_DELAY);
          }
      };
  
      public void onSensorChanged(SensorEvent event) {
          //get sensor values and store into 4 different arrays here
  
          //insert into database in background thread
          executor.execute(insertHandler);
      }
  
      @Override
      public void onCreate() {
          super.onCreate();
  
          //get sensor manager and sensors here
  
          PowerManager manager = (PowerManager) getSystemService(Context.POWER_SERVICE);
          wakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
  
          registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
  
          //Executor service and runnable for DB inserts
          executor = Executors.newSingleThreadExecutor();
          insertHandler = new InsertHandler();
      }
  
      @Override
      public IBinder onBind(Intent intent) {
          return null;
      }
  
      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
          super.onStartCommand(intent, flags, startId);
  
          startForeground(Process.myPid(), new Notification());
          registerListener();
          wakeLock.acquire();
  
          return START_STICKY;
      }
  
      @Override
      public void onDestroy() {
          //Prevent new tasks from being added to thread
          executor.shutdown();
          try {
              //Wait for all tasks to finish before we proceed
              while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
                  Log.i(TAG, "Waiting for current tasks to finish");
              }
          } catch (InterruptedException e) {
              executor.shutdownNow();
              Thread.currentThread().interrupt();
          }
  
          if (executor.isTerminated()){
              //Stop everything else once the task queue is clear
              unregisterReceiver(receiver);
              unregisterListener();
              wakeLock.release();
              dbHelper.close();
              stopForeground(true);
  
              //Once the queue is clear, I want to send a message back to the fragment to dismiss the progress dialog here
          }
      }
  
      class InsertHandler implements Runnable {
          public void run() {
              //get sensor values from 4 arrays, and insert into db here
      }
  }
  </code></pre>
  
  <p>So I want to display the dialog on the 2nd button press. Then once it is pressed again, service will stop, and I want to wait until the queue is clear and then send a dismiss event back to the fragment to dismiss the progress dialog.</p>
  
  <p>Showing the dialog is easy. I can just add progress dialog code in the <code>onClick</code> method of the fragment, before <code>stopService</code> is called</p>
  
  <p>I'm having difficulty with figuring out how to send a message back in <code>onDestroy</code> of the <code>SensorService</code> to dismiss that dialog</p>
  
  <p>Whats the best way of doing this without resorting to external libraries?</p>
  
  <p>Is there some way that the <code>BroadcastReceiver</code> I'm using in <code>SensorService</code> can be used? Or maybe it's better to create a new <code>Handler</code> in the fragment and <em>somehow</em> pass it through to the service so it can send a message back to the fragment?</p>
  
  <p>EDIT:</p>
  
  <p>I have tried the following based on one of the answers below:</p>
  
  <p>Added a <code>MessageHandler</code> class to my fragment class:</p>
  
  <pre><code>public static class MessageHandler extends Handler {
      @Override
      public void handleMessage(Message message) {
          int state = message.arg1;
          switch (state) {
              case 0:
                  stopDialog.dismiss();
                  break;
              case 1:
                  stopDialog = new ProgressDialog(mainActivity);
                  stopDialog.setMessage("Stopping...");
                  stopDialog.setTitle("Saving data");
                  stopDialog.setProgressNumberFormat(null);
                  stopDialog.setCancelable(false);
                  stopDialog.setMax(100);
                  stopDialog.show();
                  break;
          }
      }
  }
  </code></pre>
  
  <p>Created a new instance of <code>MessageHandler</code> in my fragment (tried placing this in a variety of places...same results):</p>
  
  <pre><code>public static Handler messageHandler = new MessageHandler();
  </code></pre>
  
  <p>The service is then started from my fragment using:</p>
  
  <pre><code>Intent startService = new Intent(mainActivity, SensorService.class);
  startService.putExtra("MESSENGER", new Messenger(messageHandler));
  getContext().startService(startService);
  </code></pre>
  
  <p>In my <code>SensorService</code> <code>BroadcastReceiver</code> I create the messageHandler:</p>
  
  <pre><code>Bundle extras = intent.getExtras();
  messageHandler = (Messenger) extras.get("MESSENGER");
  </code></pre>
  
  <p>Then I show the dialog at the very beginning of <code>SensorService</code> <code>onDestroy</code>:</p>
  
  <pre><code>sendMessage("SHOW");
  </code></pre>
  
  <p>and dismiss it at the very end of that same method:</p>
  
  <pre><code>sendMessage("HIDE");
  </code></pre>
  
  <p>My <code>sendMessage</code> method looks like this:</p>
  
  <pre><code>public void sendMessage(String state) {
          Message message = Message.obtain();
          switch (state) {
              case "SHOW":
                  message.arg1 = 1;
                  break;
              case "HIDE" :
                  message.arg1 = 0;
                  break;
          }
          try {
              messageHandler.send(message);
          } catch (RemoteException e) {
              e.printStackTrace();
          }
      }
  </code></pre>
  
  <p>So I can start the Service OK, but when I press it again to stop, I get this:</p>
  
  <p>java.lang.RuntimeException: Unable to stop service com.example.app.SensorService@21124f0: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.os.Messenger.send(android.os.Message)' on a null object reference</p>
  
  <p>and its referring to Line 105 of <code>SensorService</code> where I have <code>messageHandler.send(message)</code></p>
  
  <p>Thoughts on what might be wrong?</p>