2010-07-14 2 views
11

Несмотря на similar question was asked, у меня есть ситуация в разных местах: Мое приложение состоит в основном из фона Service. Я хочу начать внешнюю деятельность и вернуть результаты.Аналог startActivityForResult для обслуживания

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

  1. Создание фиктивной Activity и сохранить ссылку на него для использования его startActivityForResult. Как мы знаем, это потребляет довольно много памяти.

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

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

  4. Используйте служебные интерфейсы - сериализуйте и добавьте AIDL подключение к намерению для начала деятельности. В этом случае операция должна быть call Service directly вместо предоставления результата.

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

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

+0

Это может быть достигнуто с помощью простого хака, с использованием SharedPreferences, [SO] (http://stackoverflow.com/a/31461941/4859873) –

ответ

3

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

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

+1

Спасибо за ваши мысли! Теперь я выбираю между 2 (проще реализовать) и 4 (более безопасный/закрытый и должен быть быстрее). Я не согласен с тем, что startActivityForResult блокирует (поскольку он использует функцию обратного вызова, а не значение результата), а также Instrumentation находится в открытом API =) Спасибо! –

+1

Я имел в виду, что это не блокирование в традиционном смысле (например, блокирующий вызов io). Это блокирует концептуальный способ использования вами. – Qberticus

+0

@ Qberticus Ссылка, которую вы указали, просто ссылается на общую страницу образцов. –

16

Я думал об этом недавно при реализации 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. 
     } 

    } 

} 

При запуске деятельности в службе, создать результирующий приемник и упаковать его в намерении статистов:

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

} 
+0

спасибо за решение! одно: RESULT_OK должно быть ** - 1 ** согласно Activity.java – Philipp

+0

Хорошая точка. Я думаю, что в этом случае можно использовать любые значения, но лучше оставаться в соответствии с тем, что предоставляет платформа. Я обновил ответ. Вы также можете использовать Activity.RESULT_OK. –

+0

вам нужно добавить '@SuppressLint (« ParcelCreator ») перед классом MessageReceiver, иначе он попросит вас сделать CREATOR как [ResultReceiver] (https://developer.android.com/reference/android/os/ ResultReceiver.html) реализует Parcelable. Спасибо за ответ. – ArJ

Смежные вопросы