Как заставить службу Android общаться с активностью
Я пишу свое первое приложение для Android и пытаюсь разобраться в связи между службами и действиями. У меня есть служба, которая будет работать в фоновом режиме и делать некоторые gps и время на основе регистрации. У меня будет действие, которое будет использоваться для запуска и остановки службы.
Итак, во-первых, мне нужно быть в состоянии выяснить, если служба запущена, когда началась деятельность. Здесь есть еще несколько вопросов об этом, поэтому я думаю, что могу это понять (но не стесняйтесь давать советы).
моя реальная проблема: если действие запущено и Служба запущена, мне нужен способ для службы отправлять сообщения в действие. Простые строки и целые числа в этот момент-сообщения о состоянии в основном. Сообщения не будут происходить регулярно, поэтому я не думаю, что опрос службы-это хороший способ пойти, если есть другой способ. Я хочу, чтобы это сообщение было только тогда, когда действие было запущено пользователем - я не хочу начинать действие с Услуга. Другими словами, если вы запустите действие и Служба запущена, вы увидите некоторые сообщения о состоянии в пользовательском интерфейсе действия, когда произойдет что-то интересное. Если вы не начнете действие, вы не увидите эти сообщения (они не так интересны).
похоже, что я должен быть в состоянии определить, если служба запущена, и если да, то добавить активность в качестве слушателя. Затем удалите действие в качестве прослушивателя, когда действие приостанавливается или останавливается. Это действительно возможно? Единственный способ, которым я могу это сделать, - это реализовать Parcelable Activity и построить файл AIDL, чтобы я мог передать его через удаленный интерфейс службы. Это кажется излишним, хотя, и я понятия не имею, как деятельность должна реализовать writeToParcel() / readFromParcel().
есть ли более простой или лучший способ? Спасибо за любую помощь.
EDIT:
для тех, кто заинтересован в этом позже, есть пример кода от Google для обработки этого через AIDL в каталоге samples: / apis/app / RemoteService.java
10 ответов:
есть три очевидных способа общения с сервисами:
- С Помощью Намерения
- С помощью AIDL
- используя сам объект сервиса (как синглтон)
в вашем случае, я бы выбрал вариант 3. Сделайте статическую ссылку на сам сервис и заполните его в onCreate ():
void onCreate(Intent i) { sInstance = this; }
сделать статическую функцию
MyService getInstance()
, который возвращает статический!--2-->.затем в
Activity.onCreate()
вы запускаете службу, асинхронно дождитесь фактического запуска службы (вы можете уведомить свою службу о готовности приложения, отправив намерение в действие.) и получить его экземпляр. Когда у вас есть экземпляр, зарегистрируйте объект прослушивателя службы в службе, и вы настроены. Примечание: при редактировании представлений внутри действия вы должны изменить их в потоке пользовательского интерфейса, служба, вероятно, будет запускать свой собственный поток, поэтому вам нужно вызватьActivity.runOnUiThread()
.последнее, что вам нужно сделать, это удалить ссылка на объект прослушивателя в
Activity.onPause()
, в противном случае экземпляр контекста вашей деятельности будет протекать, а не хорошо.Примечание: этот метод полезен только тогда, когда ваше приложение/действие/задача является единственным процессом, который будет обращаться к вашей службе. Если это не так, вы должны использовать Вариант 1. или 2.
спрашивающий, вероятно, уже давно прошел мимо этого, но в случае, если кто-то еще ищет это...
есть другой способ справиться с этим, который я думаю, может быть самым простым.
добавить BroadcastReceiver к вашей деятельности. Зарегистрируйте его, чтобы получить некоторые пользовательские намерения в onResume и отменить регистрацию в onPause. Затем отправьте это намерение из своей службы, когда вы хотите отправить свои обновления статуса или что у вас есть.
убедитесь, что вы не были бы несчастный, если какое-то другое приложение слушало ваше намерение (может ли кто-нибудь сделать что-нибудь злое?), но помимо этого, вы должны быть в порядке.
образец кода был запрошен:
у меня на службе есть:
// Do stuff that alters the content of my local SQLite Database sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT));
(RefreshTask.REFRESH_DATA_INTENT - это просто постоянная строка.)
в моей деятельности прослушивания, я определяю мой BroadcastReceiver:
private class DataUpdateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(RefreshTask.REFRESH_DATA_INTENT)) { // Do stuff - maybe update my view based on the changed DB contents } } }
Я объявляю мой приемник в верхней части класса:
private DataUpdateReceiver dataUpdateReceiver;
Я переопределить onResume добавить:
if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver(); IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT); registerReceiver(dataUpdateReceiver, intentFilter);
и я переопределяю onPause, чтобы добавить:
if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);
теперь моя деятельность слушает мой сервис, чтобы сказать: "Эй, иди обновить себя."Я мог бы передать данные в Intent вместо обновления таблиц базы данных, а затем вернуться, чтобы найти изменения в моей деятельности, но поскольку я хочу, чтобы изменения сохранялись в любом случае, имеет смысл передавать данные через БД.
использовать
LocalBroadcastManager
чтобы зарегистрировать приемник для прослушивания трансляции, отправленной из localservice внутри вашего приложения, ссылка идет здесь:http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html
Я удивлен, что никто не дал ссылку на библиотеку шины событий Otto
Я использую это в своих приложениях для android, и он работает без проблем.
использование мессенджера-это еще один простой способ связи между сервисом и активностью.
в действии создайте обработчик с соответствующим мессенджером. Это позволит обрабатывать сообщения от вашего сервиса.
class ResponseHandler extends Handler { @Override public void handleMessage(Message message) { Toast.makeText(this, "message from service", Toast.LENGTH_SHORT).show(); } } Messenger messenger = new Messenger(new ResponseHandler());
мессенджер можно передать службе, прикрепив его к сообщению:
Message message = Message.obtain(null, MyService.ADD_RESPONSE_HANDLER); message.replyTo = messenger; try { myService.send(message); catch (RemoteException e) { e.printStackTrace(); }
полный пример можно найти в демонстрациях API:MessengerService и MessengerServiceActivity. См. полный пример для того, как MyService с работы.
другой метод, который не упоминается в других комментариях, заключается в привязке к службе из действия с помощью bindService() и получении экземпляра службы в обратном вызове ServiceConnection. Как описано здесь http://developer.android.com/guide/components/bound-services.html
другим способом может быть использование наблюдателей с поддельным классом модели через действие и саму службу, реализуя вариацию шаблона MVC. Я не знаю, лучший ли это способ сделать это, но это то, что сработало для меня. Если вам нужен какой-то пример, попросите его, и я что-нибудь опубликую.
чтобы следить за ответом @MrSnowflake с примером кода. это XABBER теперь с открытым исходным кодом
Application
класс. ЭлементApplication
класс централизует и координируетListeners
и ManagerInterfaces и многое другое. Менеджеры всех видов динамически загружаются.Activity´s
начатый в Xabber сообщит в каком типеListener
они. И когдаService
запустите его отчет вApplication
класс, как началась. Теперь, чтобы отправить сообщение вActivity
все, что вам нужно сделать, это сделать свойActivity
статьlistener
какого типа вам нужно. ВOnStart()
OnPause()
Регистрация/unreg. ЭлементService
можно спросить уApplication
класс простоlistener
он должен говорить, и если он там, то деятельность готова принять.проходит через
Application
класс вы увидите, что есть добыча больше происходит, чем это.
Как упоминалось Мадхур, вы можете использовать автобус для связи.
в случае использования автобуса у вас есть несколько вариантов:
библиотека шины событий Otto (устарела в пользу RxJava)
EventBus зеленого робота
http://greenrobot.org/eventbus/
NYBus (RxBus, реализовано с помощью RxJava. очень похоже на то EventBus)
кроме того LocalBroadcastManager, Event Bus и Messenger уже отвечал на этот вопрос,мы можем использовать В Ожидании Намерениях для связи со службой.
Как уже упоминалось здесь в моем блоге
связь между обслуживанием и деятельностью можно сделать используя PendingIntent.Для этого мы можем использовать createPendingResult().createPendingResult() создает новый Объект PendingIntent, который вы можете для комфорта в использовании и отправить результат данных обратно к вашей деятельности внутри onActivityResult(int, int, Намерение) обратный вызов.Поскольку PendingIntent является Parcelable , и может поэтому вкладывайте в намерение дополнительно,ваша деятельность может пройти это PendingIntent к службе.Служба, в свою очередь, может позвонить отправить() метод на PendingIntent для уведомления о деятельности через onActivityResult события.
активность
public class PendingIntentActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PendingIntent pendingResult = createPendingResult( 100, new Intent(), 0); Intent intent = new Intent(getApplicationContext(), PendingIntentService.class); intent.putExtra("pendingIntent", pendingResult); startService(intent); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 100 && resultCode==200) { Toast.makeText(this,data.getStringExtra("name"),Toast.LENGTH_LONG).show(); } super.onActivityResult(requestCode, resultCode, data); } }
сервис
public class PendingIntentService extends Service { private static final String[] items= { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus" }; private PendingIntent data; @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { data = intent.getParcelableExtra("pendingIntent"); new LoadWordsThread().start(); return START_NOT_STICKY; } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { super.onDestroy(); } class LoadWordsThread extends Thread { @Override public void run() { for (String item : items) { if (!isInterrupted()) { Intent result = new Intent(); result.putExtra("name", item); try { data.send(PendingIntentService.this,200,result); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); } SystemClock.sleep(400); } } } } }