Аналог функции 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();
}
}