65

Fundamentally, I would like to establish a callback to an Activity from an IntentService. My question is very similar to the one answered here:

Restful API service

However, in the answer code, the activity code is seen as implementing a ResultReceiver. Unless I'm missing something, ResultReceiver is actually a class, so it cannot perform this implementation.

So essentially, I'm asking what would be the correct way to wire up a ResultReceiver to that service. I get confused with Handler and ResultReceiver concepts with respect to this. Any working sample code would be appreciated.

Community
  • 1
  • 1
Bill Eisenhauer
  • 6,183
  • 2
  • 30
  • 28

4 Answers4

126
  1. You need to make custom resultreceiver class extended from ResultReceiver

  2. then implements the resultreceiver interface in your activity

  3. Pass custom resultreceiver object to intentService and in intentservice just fetch the receiver object and call receiver.send() function to send anything to the calling activity in Bundle object.

    here is customResultReceiver class :

     public class MyResultReceiver extends ResultReceiver {
    
        private Receiver mReceiver;
    
        public MyResultReceiver(Handler handler) {
            super(handler);
            // TODO Auto-generated constructor stub
        }
    
        public interface Receiver {
            public void onReceiveResult(int resultCode, Bundle resultData);
    
        }
    
        public void setReceiver(Receiver receiver) {
            mReceiver = receiver;
        }
    
        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
    
            if (mReceiver != null) {
                mReceiver.onReceiveResult(resultCode, resultData);
            }
        }
    
    }
    

implements the Myresultreceiver.receiver interface in you activity, create a class variable

Public MyResultReceiver mReceiver;

initialize this variable in onCreate:

mReceiver = new MyResultReceiver(new Handler());

mReceiver.setReceiver(this);

Pass this mReceiver to the intentService via:

intent.putExtra("receiverTag", mReceiver);

and fetch in IntentService like:

ResultReceiver rec = intent.getParcelableExtra("receiverTag");

and send anything to activity using rec as:

Bundle b=new Bundle();
rec.send(0, b);

this will be received in onReceiveResult of the activity. You can view complete code at:IntentService: Providing data back to Activity

Edit: You should call setReceiver(this) in onResume and setReceiver(null) in onPause() to avoid leaks.

SohailAziz
  • 8,034
  • 6
  • 41
  • 43
  • 2
    This is very useful for me. Thanks! Nice explaination! – herbertD Sep 13 '12 at 15:35
  • this should be the correct answer, you need to use a handler to run the callback in the ui(main) thread. you cannot modify the user interface if you directly attach a ResultReceiver to your intent. – Kemal Dağ Apr 19 '13 at 09:06
  • 1
    So does ResultReceiver always go hand-in-hand with an IntentService? And is there a less complicated way to accomplish what the ResultReceiver does? I mean, this architecture seems too convoluted and has too many circular dependencies. – IgorGanapolsky Dec 09 '13 at 19:36
  • 7
    But when `Activity` would be recreated you will never got your result back and moreover - leak the destroyed `Activity`. – Eugene Dec 23 '13 at 09:33
  • @IgorGanapolsky instead of this local broadcasat is better option. – SohailAziz Jan 10 '14 at 08:49
  • @KemalDağ if you notice, handler is passed in mReceiver = new MyResultReceiver(new Handler()); so onReceiveResult will be called on handler's thread thats activity. – SohailAziz Jan 10 '14 at 09:56
  • @SohailAziz , There is another approach by using sendBroadCast(intent) and a broadcast receiver in the activity. I used it but the performance was so bad and I got a lot of "doing much work on main thread exception". This approach I think is better. Can you explain the difference between them please. I used it with a Service not IntentService, service that sends location updates. – MSaudi Feb 05 '14 at 08:50
  • @user489775645 these are two totally different approaches, this one is based on handler pattern the other is receiver pattern. You can use local broadcast (available via support lib) sendLocalBroadcast(...), a much better option. – SohailAziz Feb 06 '14 at 08:22
  • 4
    [This answer](http://stackoverflow.com/a/23059313/1428549) addresses the screen rotation/activity recreation scenario, by taking advantage of the fact that [ResultReceiver](http://developer.android.com/reference/android/os/ResultReceiver.html) implements `Parcelable`. It can be saved during `onSaveInstanceState(Bundle outState)` and recreated from `Bundle savedInstanceState`. – villoren Oct 17 '14 at 02:11
  • Sohel, can i use a simple interface to recv data from Service to Activity ? – theapache64 Mar 10 '15 at 11:16
  • Since most services performing a background task will be involving a different thread it might be preferable to make sure that all access to `mReceiver ` is done with safe publishing of variables. In other words the variable should be either `volatile`, accessed via `synchronized` blocks or through a `AtomicReference`. – Emmanuel Rodriguez Feb 04 '16 at 06:17
  • I'm confused by the usage of `setReceiver()`. I don't think it's necessary. I think you merely override `onReceiveResult()` and you're good to go. I think Eric Bowman's answer is simpler and more correct. – Edward Falk Mar 22 '16 at 18:28
  • OK, I read your blog post. You're using the setReceiver() functionality to enable the receipt of results only when the Activity is visible (between onResume() and onPause()). You might also want to consider passing the ResultReceiver across instances of the Activity via [onRetainNonConfigurationInstance()](http://developer.android.com/reference/android/app/Activity.html#onRetainNonConfigurationInstance()) – Edward Falk Mar 22 '16 at 21:05
  • I don't see how `setReceiver(null)` would prevent leaks.. `onReceiveResult` of superclass will still get called even if activity is destroyed. You just won't notice it because your interface will be removed but it will still get called. – Micro May 20 '16 at 16:36
  • 1
    If you save mReceiver as a instance variable in the Activity and you pass mReceiver to the IntentService, wouldn't you still have an implicit reference to the Activity from the IntentService through mReceiver? – mco Jun 28 '16 at 07:47
  • A limitation here is that `ResultReceiver` cannot be cast to `MyResultReceiver` after de-parcelization correct? – the_prole Dec 06 '21 at 23:32
23

You override a method by subclassing. It doesn't have to be an interface to do that.

For example:

intent.putExtra(StockService.REQUEST_RECEIVER_EXTRA, new ResultReceiver(null) {
    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        if (resultCode == StockService.RESULT_ID_QUOTE) {
            ...
        }
    }
});
  • 1
    As far as I can tell, this is only have of the answer - can you show the corresponding IntentService side code that unmarshalls the ResultReceiver for the send() method? That is causing me problems. – mobibob Jan 09 '11 at 15:15
  • ... half (sic) ... I went back and studied the sample code from Google IO iosched and realized that I was making my "detattachable-resultreceiver" way too complicated. As it turns out, on the server side, the extra is simply retrieved with the intent.getExtra("receiver-key") as one would expect. No special magic. – mobibob Jan 09 '11 at 16:29
  • 1
    It is quite simple and handy. – Eric Bowman - abstracto - Jan 09 '11 at 17:50
  • 2
    But the anonymous class holds an implicit reference to the enclosing class. It seems if this class is `Activity` we have the memory leakage during the configuration changes. – Stan Mots Oct 17 '16 at 13:38
12

I have created a simple example that demonstrates how to use ResultReceiver.

MainActivity:

public class MainActivity extends AppCompatActivity {

    private final static String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent serviceIntent = new Intent(this, MyService.class);
        serviceIntent.putExtra("logName", "MAIN_ACTIVITY");
        serviceIntent.putExtra(MyService.BUNDLED_LISTENER, new ResultReceiver(new Handler()) {
            @Override
            protected void onReceiveResult(int resultCode, Bundle resultData) {
                super.onReceiveResult(resultCode, resultData);

                if (resultCode == Activity.RESULT_OK) {
                    String val = resultData.getString("value");
                    Log.i(TAG, "++++++++++++RESULT_OK+++++++++++ [" + val + "]");
                } else {
                    Log.i(TAG, "+++++++++++++RESULT_NOT_OK++++++++++++");
                }
            }
        });
        startService(serviceIntent);
    }
}

MyService:

public class MyService extends Service {

    private final static String TAG = MyService.class.getSimpleName();
    public final static String BUNDLED_LISTENER = "listener";

    @Override
    public void onCreate() {
        super.onCreate();

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        String logName = intent.getStringExtra("logName");
        ResultReceiver receiver = intent.getParcelableExtra(MyService.BUNDLED_LISTENER);

        Bundle bundle = new Bundle();
        bundle.putString("value", "30");
        receiver.send(Activity.RESULT_OK, bundle);
        return Service.START_NOT_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}
Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
Amrmsmb
  • 1
  • 27
  • 104
  • 226
-4

for use Resulteceiver in android

  1. Create SomeResultReceiver extends from resultReceiver

  2. Create interface someReceiver with on method for example onReceivResult(int resultCode,Bundle resultData);

3.use someReceiver in someResultreceiver

  1. create someService extends IntentService and use someresultReceiver.send() method for send result from service to someOne class (ex: MyActivity)

  2. Implement somereceiver on Activity

6.instantiation someResultReceiver in MyActivity class and setreceiver

  1. startService with Intent and putExtra someResultreceiver instanse

for more details ResultReceiver Class see enter link description here