类似于startActivityForResult for Service

时间:2010-07-14 16:04:38

标签: android

尽管similar question was asked,我有不同的情况: 我的应用主要包含背景Service。我想开始外部活动并取回结果。

我看到了几个选项:

  1. 创建虚拟Activity并继续引用它以使用其startActivityForResult。正如我们所知,这会占用大量内存。

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

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

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

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

    也许你还有其他想法吗?

2 个答案:

答案 0 :(得分:19)

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

这并没有明确记录,但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.
        }

    }

}

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

最后,在活动中,解压缩接收器并使用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();
    }

}

答案 1 :(得分:4)

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

如果您使用android local service pattern,那么您可以让您的活动获得Service的引用,然后在执行其工作后调用特定函数。尝试使用您的选项3将违背框架为您提供的内容。