Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the Android ResultReceiver callback work if it is passed through a Bundle as Parcelable?

I have passed a ResultReceiver from my Activity to my Service.

Activity code example to pass the ResultReceiver to my service so the service can make a callback to the Activity:

ResultReceiver receiver = new ResultReceiver(new Handler()) {
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        //process results
    }
}

Intent instructionServiceIntent = new Intent(context, InstructionService.class);
instructionServiceIntent.putExtra("receiver", receiver);
context.startService(instructionServiceIntent);

InstructionService code example:

protected void onHandleIntent(Intent intent) {
    Bundle parameters = intent.getExtras();
    ResultReceiver resultReceiver = parameters.getParcelable("receiver");
    resultReceiver.send(METHOD_STATUS_RUNNING, Bundle.EMPTY);
}

Now, this works fine as when I call the resultReceiver.send method in my service, the corresponding onReceiveResult method in the activity is executed.

My question is, how does this work? As far as I understand, the ResultReceiver is being passed from the activity to the service as a Parcelable, which means its a "copy" of that object, and not a reference to the original ResultReceiver object which was created in the Activity. Therefore, how can a call to the send method on the copy of the ResultReceiver in the service class, make it so that the original ResultReceiver object in the activity runs it's onReceiveResult method?

like image 863
dleerob Avatar asked May 23 '14 06:05

dleerob


1 Answers

ResultReciver implements Parcelable so it can be passed in an intent.

For the startservice it's easier just to load the intent used to start the service with data just like an intent sent to an activity then in the service process the intent data in onStartComand();

If you want to send messages back in forth to a service you should bind to the service startboundservice and in the service return a handler in onBind() to the activity.

This is where the magic of ResultReceiver works. It's just a message that has prepopulated handler so it can be serialized and passed back.

If you just want to get a reply from some fragment class that you create on the fly or something out of the norm actually serialize a callback you can pass in a message populated with the handler that will process the message.

Below I create a handler then create a message with the handler. Then I start a fragment that does something obscure and finally returns the message. So ineffect the message is just a copy but it is actually a callback that is passed in a bundle.

This is new code so I don't know how stable it is...

public void get_UserEmail(final MyGooglePlayCallback userEmailCallback) {

    //handle message gets called when the fragment calls msg.sendToTarget()
    //
    class JX implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            Bundle b = msg.getData();
            String s = "";
            if (b != null) {
                s = b.getString("email");
            }
            msg.recycle();
            userEmailCallback.onComplete(s);

            return true;
        }
    }
    JX jx = new JX();

    Handler h = new Handler(jx);

    final Message msg = Message.obtain(h);

    MyGooglePlayCallback cb;


    // start the account picker to get the email
    String[] accountTypes = new String[]{"com.google"};
    Intent intentx = AccountPicker.newChooseAccountIntent(null, null,
            accountTypes, false, null, null, null, null);

    MyFragmentActivityForResult2 f = MyFragmentActivityForResult2.newInstance(
            intentx, REQUEST_CODE_PICK_ACCOUNT, msg);

    FragmentTransaction fragmentTransaction = fragManager
            .beginTransaction();
    fragmentTransaction.add(f, "xx" + REQUEST_CODE_PICK_ACCOUNT);
    fragmentTransaction.commit();
}







    public static class MyFragmentActivityForResult2 extends Fragment {

    private Message msg;
    private int requestCode;
    private Intent intent;

    static MyFragmentActivityForResult2 newInstance(Intent intent, int requestCode,
                                                    Message message) {

        MyFragmentActivityForResult2 f = new MyFragmentActivityForResult2();
        Bundle args = new Bundle();
        args.putInt("requestCode", requestCode);
        args.putParcelable("message", message);
        args.putParcelable("intent", intent);
        f.setArguments(args);
        return f;
    }

    MyFragmentActivityForResult2() {
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        Bundle b = this.getArguments();
        requestCode = b.getInt("requestCode");
        msg = b.getParcelable("message");
        intent = b.getParcelable("intent");
        startActivityForResult(intent, requestCode);
    }


    @Override
    public void onActivityResult(int requestCode, int resultCode,
                                 Intent data) {

        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == this.requestCode) {
            String mEmail = "";
            if (resultCode == Activity.RESULT_OK) {
                mEmail = data
                        .getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
            }
            Bundle b = new Bundle();
            b.putString("email", mEmail);
            msg.setData(b);
            msg.arg1 = requestCode;
            msg.arg2 = resultCode;
            msg.sendToTarget();
        }

        fragManager.beginTransaction().remove(this).commit();
    }
}
like image 146
danny117 Avatar answered Oct 20 '22 22:10

danny117