类似于startActivityForResult for Service

Ale*_*kov 13 android

尽管问了类似的问题,但我有不同的情况:我的应用程序主要由背景组成Service.我想开始外部活动并取回结果.

我看到几个选项:

  1. 创建虚拟Activity并保持对它的引用以使用它startActivityForResult.据我们所知,这会占用大量内存.

  2. 使用Broadcast Intents而不是Android的结果基础架构:要求客户端活动在关闭之前广播其结果.这种打破了这个想法而不是那么高效.

  3. Instrumentation直接使用- 尝试将startActivityForResult中的代码复制到我的服务中.

  4. 使用服务接口 - 序列化并添加AIDL与Intent的连接以启动活动.在这种情况下,Activity应该直接调用Service而不是提供结果.

第三种方法让我感觉更接近Android,但我不确定它是否可行 - 服务没有它的Instrumentation,并且默认实现似乎总是返回null.

也许你还有其他想法吗?

Leo*_*ilä 20

最近在实施具有三条腿授权流程的帐户验证器时,我一直在考虑这个问题.将结果发送回服务进行处理比在活动中处理结果更好.它还提供了更好的关注点分离.

这并没有明确记录,但Android提供了一种简单的方式来发送和接收任何地方(包括服务)的结果ResultReceiver.

我发现它比传递活动更清洁,因为这总是带来泄漏这些活动的风险.另外,调用具体方法的灵活性较低.

ResultReceiver在服务中使用,您需要对其进行子类化并提供一种处理接收结果的方法,通常在内部类中:

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.
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

当您在服务中启动活动时,创建结果接收器并将其打包到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);
}
Run Code Online (Sandbox Code Playgroud)

最后,在活动中,解压缩接收器并用于ResultReceiver#send(int, Bundle)发回结果.

您可以随时发送结果,但在此之前我选择在完成之前执行此操作:

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();
    }

}
Run Code Online (Sandbox Code Playgroud)


Ric*_*ler 4

我认为选项2是android上最惯用的方式。使用startActivityForResultfrom anActivity是同步/阻塞调用,即父活动等待并且在子活动完成之前不执行任何操作。当从 a 工作并Service与活动交互时,您主要进行异步/非阻塞调用,即服务要求完成一些工作,然后等待信号告诉它可以继续。

如果您使用的是android 本地服务模式,那么您可以让您的活动获取 的引用Service,然后在执行其工作后调用特定的函数。尝试选项 3 将与框架为您提供的内容背道而驰。