Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Analog of startActivityForResult for Service

Tags:

android

Despite similar question was asked, I have differnet situation: My app consists mostly of a background Service. I want to start external activities and get results back.

I see several options:

  1. Create dummy Activity and keep reference to it for using its startActivityForResult. This consumes quite a lot of memory, as we know.

  2. Use Broadcast Intents instead of Android's results infrastructure: ask client activities to broadcast their results before closing. This kind of breaks the idea and not so performance-efficient.

  3. Use Instrumentation directly - try to copy code from startActivityForResult into my Service.

  4. Use Service interfaces - serialize and add AIDL connection to the Intent for starting an Activity. In this case Activity should call Service directly instead of providing result.

The third approach feels closer to Android for me, but I'm not sure if it's possible to do - Service does not have its Instrumentation, and default implementation seems to always return null.

Maybe you have any other ideas?

like image 708
Alexander Kosenkov Avatar asked Jul 14 '10 16:07

Alexander Kosenkov


People also ask

What is the replacement for startActivityForResult?

Among the contracts available, the StartActivityForResult contract is the replacement to the startActivityForResult . The callback is to be called once the activity result is available.

What is purpose of startActivityForResult () function?

By the help of android startActivityForResult() method, we can send information from one activity to another and vice-versa. The android startActivityForResult method, requires a result from the second activity (activity to be invoked).

What is request code startActivityForResult?

The request code is any int value. The request code identifies the return result when the result arrives. ( You can call startActivityForResult more than once before you get any results. When results arrive, you use the request code to distinguish one result from another.

Which API version is startActivityForResult deprecated?

activity:activity-ktx to 1.2. 0 . It has deprecated startActivityForResult in favour of registerForActivityResult . It was one of the first fundamentals that any Android developer has learned, and the backbone of Android's way of communicating between two components.


2 Answers

I’ve been thinking about this recently when implementing account authenticators with three-legged authorisation flows. Sending a result back to the service for processing performs better than processing it in the activity. It also provides a better separation of concerns.

It’s not that clearly documented, but Android provides an easy way to send and receive results anywhere (including services) with ResultReceiver.

I’ve found it to be a lot cleaner than passing activities around, since that always comes with the risk of leaking those activities. Additionally, calling concrete methods is less flexible.

To use ResultReceiver in a service, you’ll need to subclass it and provide a way to process the received result, usually in an inner class:

public class SomeService extends Service {

    /**
     * Code for a successful result, mirrors {@link Activity.RESULT_OK}.
     */
    public static final int RESULT_OK = -1;

    /**
     * Key used in the intent extras for the result receiver.
     */
    public static final String KEY_RECEIVER = "KEY_RECEIVER";

    /**
     * Key used in the result bundle for the message.
     */
    public static final String KEY_MESSAGE = "KEY_MESSAGE";

    // ...

    /**
     * Used by an activity to send a result back to our service.
     */
    class MessageReceiver extends ResultReceiver {

        public MessageReceiver() {
            // Pass in a handler or null if you don't care about the thread
            // on which your code is executed.
            super(null);
        }

        /**
         * Called when there's a result available.
         */
        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            // Define and handle your own result codes
            if (resultCode != RESULT_OK) {
                return;
            }

            // Let's assume that a successful result includes a message.
            String message = resultData.getString(KEY_MESSAGE);

            // Now you can do something with it.
        }

    }

}

When you start an activity in the service, create a result receiver and pack it into the intent extras:

/**
 * Starts an activity for retrieving a message.
 */
private void startMessageActivity() {
    Intent intent = new Intent(this, MessageActivity.class);

    // Pack the parcelable receiver into the intent extras so the
    // activity can access it.
    intent.putExtra(KEY_RECEIVER, new MessageReceiver());

    startActivity(intent);
}

And finally, in the activity, unpack the receiver and use ResultReceiver#send(int, Bundle) to send a result back.

You can send a result at any time, but here I've chosen to do it before finishing:

public class MessageActivity extends Activity {

    // ...

    @Override
    public void finish() {
        // Unpack the receiver.
        ResultReceiver receiver =
                getIntent().getParcelableExtra(SomeService.KEY_RECEIVER);

        Bundle resultData = new Bundle();

        resultData.putString(SomeService.KEY_MESSAGE, "Hello world!");

        receiver.send(SomeService.RESULT_OK, resultData);

        super.finish();
    }

}
like image 158
Leo Nikkilä Avatar answered Oct 26 '22 20:10

Leo Nikkilä


I think option 2 is the most idiomatic way on android. Using startActivityForResult from an Activity is a synchronous/blocking call, i.e., the parent activity waits and does not do anything until the child is done. When working from a Service and interacting with activities your primarily doing asynchronous/non-blocking calls, i.e., the service calls out for some work to be done and then waits for a signal to tell it that it can continue.

If you are using the android local service pattern then you can have your activities acquire a reference of the Service and then call a specific function after it has performed its work. Attempting your option 3 would be counter to what the framework provides for you.

like image 25
Rich Schuler Avatar answered Oct 26 '22 18:10

Rich Schuler