Android Service extends ResultReceiver for IntentService, how to implement CREATOR?

Matthias Ronge picture Matthias Ronge · Nov 24, 2015 · Viewed 7.2k times · Source

My app relies on a Service which stays in sync with external hardware in the background. Because the service operates on the main Thread, it does any heavy work asynchronously using an IntentService. Here is a minimalized example of the code to explain the control flow:

public class MyService extends Service {
  private final Handler handler = new Handler();

  void someMethodDoesCallBarAsynchronously(){
    Intent i = new Intent(this, MyIntentService.class);
    i.putAction(ACTION_FOO);
    i.putExtra(EXTRA_RECEIVER, new MyResultReceiver(handler));
    startService(toGetScannerStatus);
  }

  void receivedFooResult(String bar){
    //...
  }

  public class MyResultReceiver extends ResultReceiver{

    public MyResultReceiver(Handler handler){
      super(handler);
    }

    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {
      super.onReceiveResult(resultCode, resultData);

      switch(resultCode){
        case RESULT_CODE_FOO:
          receivedFooResult(resultData.getString(FOO_RESULT));
        break;
      }
    }
  }
}

public class MyIntentService extends IntentService {

  public MyIntentService(){super("MyIntentService");}

  @Override
  protected void onHandleIntent(Intent intent) {
    switch (intent.getAction()) {
      case ACTION_FOO:
        ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RECEIVER);
        Bundle bundle = new Bundle()
        bundle.putString(FOO_RESULT, foo());
        receiver.send(RESULT_CODE_FOO, b);
      break;
  }
}

Now, after half a year and some updates, Android Studio complains that MyResultReceiver implements Parcelable but does not provide a CREATOR field. Problem one, MyResultReceiver is an inner class, so I have to put it into an own class. I cannot make it static because it must hold a reference to MyService. That is still quite easy:

public class MyService extends Service {
  private final Handler handler = new Handler();

  void someMethodDoesCallBarAsynchronously(){
    Intent i = new Intent(this, MyIntentService.class);
    i.putAction(ACTION_FOO);
    i.putExtra(EXTRA_RECEIVER, new MyResultReceiver(this, handler));
    startService(toGetScannerStatus);
  }

  void receivedFooResult(String bar){
    //...
  }
}

public class MyResultReceiver extends ResultReceiver{
  private final MyService myService;

  public MyResultReceiver(MyService s, Handler handler){
    super(handler);
    this.myService = myService;
  }

  @Override
  protected void onReceiveResult(int resultCode, Bundle resultData) {
    super.onReceiveResult(resultCode, resultData);

    switch(resultCode){
      case RESULT_CODE_FOO:
        myService.receivedFooResult(resultData.getString(FOO_RESULT));
      break;
    }
  }
}

Now I can add a public static field. But how to implement CREATOR correctly?

public class MyResultReceiver extends ResultReceiver {

  public static final Parcelable.Creator<MyResultReceiver> CREATOR
    = new Parcelable.Creator<MyResultReceiver>() {
        public MyResultReceiver[] newArray(int size) {
          return new MyResultReceiver[size];
        }
        public MyResultReceiver createFromParcel(Parcel in) {
          // ???
        }
      };

If you want to: Why is this necessary? What will it be used for?

Answer

pskink picture pskink · Nov 25, 2015

your current architecture is too complex imho: you could easily do that in one Service but it is up to you, when it comes to your problem: you don't need to create any CREATOR as what you got is a lint warning, just add SuppressLint annotation like this:

@SuppressLint("ParcelCreator")
public class MyResultReceiver extends ResultReceiver {