Аналог функции startActivityForResult for Service

Несмотря на был задан аналогичный вопрос, у меня ситуация differnet: Мое приложение состоит в основном из фона Service. Я хочу начать внешние действия и вернуть результаты.

Я вижу несколько вариантов:

  • Создайте манекен Activity и сохраните ссылку на него для использования startActivityForResult. Это, как известно, занимает довольно много памяти.

  • Используйте Broadcast Intents вместо инфраструктуры результатов Android: попросите действия клиента транслировать результаты до закрытия. Этот вид ломает идею и не настолько эффективен.

  • Используйте Instrumentation напрямую - попробуйте скопировать код из startActivityForResult в мою службу.

  • Используйте служебные интерфейсы - сериализуйте и добавьте AIDL подключение к Intent для запуска Activity. В этом случае мероприятие должно напрямую вызвать службу вместо предоставления результата.

Третий подход ближе для Android для меня, но я не уверен, что это возможно - у Service нет инструментария, и реализация по умолчанию, похоже, всегда возвращает null.

Возможно, у вас есть другие идеи?

Ответ 1

Я думаю, что вариант 2 - самый идиоматический способ для Android. Использование startActivityForResult из Activity является синхронным/блокирующим вызовом, то есть родительская активность ждет и ничего не делает до тех пор, пока ребенок не будет выполнен. Когда вы работаете с Service и взаимодействуете с действиями, в основном выполняете асинхронные/неблокирующие вызовы, т.е. Служба вызывает некоторую работу, а затем ждет сигнала, чтобы сообщить ей, что она может продолжаться.

Если вы используете шаблон локальной службы android, вы можете получить свои действия, чтобы получить ссылку Service, а затем вызвать после того, как он выполнил свою работу. Попытка вашего варианта 3 будет противоречить тому, что предоставляет вам рамка.

Ответ 2

Я думал об этом недавно при внедрении учетных записей с трехсторонними потоками авторизации. Отправка результата обратно в сервис для обработки выполняется лучше, чем обработка его в активности. Это также обеспечивает лучшее разделение проблем.

Это не так четко документировано, но Android предоставляет простой способ отправки и получения результатов в любом месте (включая службы) с помощью ResultReceiver.

Ive показало, что это намного чище, чем передача действий вокруг, так как это всегда сопряжено с риском утечки этих действий. Кроме того, вызов конкретных методов менее гибкий.

Чтобы использовать 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 a result available.
         */
        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            // Define and handle your own result codes
            if (resultCode != RESULT_OK) {
                return;
            }

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

            // Now you can do something with it.
        }

    }

}

Когда вы запускаете действие в службе, создайте ресивер результата и упакуйте его в дополнительные настройки:

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

}