17

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?

Matthias Ronge
  • 9,403
  • 7
  • 47
  • 63
  • why? what do you need `MyIntentService` for? why not to do a `heavy work` in `HandlerThread` inside `MyService`? no needs for `ResultReceiver`, `CREATOR` etc (if you take a look into `IntentService` you will see it has nothing but `HandlerThread` inside) – pskink Nov 24 '15 at 10:22
  • Why? Because this was the first way I got all things to work the way I want them to. This is the existing design of the working application which is large, tested and productive, and cannot be changed easily. (Above snippet is only to demonstate the control flow.) Android framework provides IntentService class and ResultReceiver. So question is how to use them in a sensible way. I thought I had understood it, see above, but the new requirement does not fit in. So how is the design intended if a Service wants to load off work to an IntentService and receive results though a ResultsReceiver? – Matthias Ronge Nov 24 '15 at 14:46
  • try `@SuppressLint("ParcelCreator") class MyResultReceiver extends ResultReceiver {...` but seriously consider simplified architecture (without two Services) – pskink Nov 24 '15 at 15:38
  • @psklink perhaps you make this an answer? – Matthias Ronge Nov 25 '15 at 09:11

1 Answers1

30

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 {
pskink
  • 23,874
  • 6
  • 66
  • 77