Как заставить службу Android общаться с активностью


Я пишу свое первое приложение для Android и пытаюсь разобраться в связи между службами и действиями. У меня есть служба, которая будет работать в фоновом режиме и делать некоторые gps и время на основе регистрации. У меня будет действие, которое будет использоваться для запуска и остановки службы.

Итак, во-первых, мне нужно быть в состоянии выяснить, если служба запущена, когда началась деятельность. Здесь есть еще несколько вопросов об этом, поэтому я думаю, что могу это понять (но не стесняйтесь давать советы).

моя реальная проблема: если действие запущено и Служба запущена, мне нужен способ для службы отправлять сообщения в действие. Простые строки и целые числа в этот момент-сообщения о состоянии в основном. Сообщения не будут происходить регулярно, поэтому я не думаю, что опрос службы-это хороший способ пойти, если есть другой способ. Я хочу, чтобы это сообщение было только тогда, когда действие было запущено пользователем - я не хочу начинать действие с Услуга. Другими словами, если вы запустите действие и Служба запущена, вы увидите некоторые сообщения о состоянии в пользовательском интерфейсе действия, когда произойдет что-то интересное. Если вы не начнете действие, вы не увидите эти сообщения (они не так интересны).

похоже, что я должен быть в состоянии определить, если служба запущена, и если да, то добавить активность в качестве слушателя. Затем удалите действие в качестве прослушивателя, когда действие приостанавливается или останавливается. Это действительно возможно? Единственный способ, которым я могу это сделать, - это реализовать Parcelable Activity и построить файл AIDL, чтобы я мог передать его через удаленный интерфейс службы. Это кажется излишним, хотя, и я понятия не имею, как деятельность должна реализовать writeToParcel() / readFromParcel().

есть ли более простой или лучший способ? Спасибо за любую помощь.

EDIT:

для тех, кто заинтересован в этом позже, есть пример кода от Google для обработки этого через AIDL в каталоге samples: / apis/app / RemoteService.java

10 221

10 ответов:

есть три очевидных способа общения с сервисами:

  1. С Помощью Намерения
  2. С помощью AIDL
  3. используя сам объект сервиса (как синглтон)

в вашем случае, я бы выбрал вариант 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

http://square.github.io/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)

http://square.github.io/otto/

EventBus зеленого робота

http://greenrobot.org/eventbus/

NYBus (RxBus, реализовано с помощью RxJava. очень похоже на то EventBus)

https://github.com/MindorksOpenSource/NYBus

кроме того 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);

                }
            }
        }
    }
}