Как сделать так, чтобы Android-сервис общался с Activity - PullRequest
233 голосов
/ 17 марта 2010

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

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

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

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

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

РЕДАКТИРОВАТЬ:

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

Ответы [ 13 ]

245 голосов
/ 19 января 2011

Возможно, спрашивающий уже давно прошел мимо этого, но в случае, если кто-то другой ищет это ...

Есть еще один способ справиться с этим, который, я думаю, может быть самым простым.

Добавьте BroadcastReceiver к вашей активности. Зарегистрируйте его, чтобы получить пользовательское намерение в onResume и отмените регистрацию в onPause. Затем отправьте это намерение из вашей службы, если вы хотите отправить обновления своего статуса или что у вас есть.

Удостоверьтесь, что вы не будете несчастны, если какое-то другое приложение прислушается к вашему Intent (может кто-нибудь сделать что-нибудь злое?), Но кроме этого, вы должны быть в порядке.

Образец кода был запрошен:

На моем служении у меня есть это:

// 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 вместо обновления таблиц базы данных и затем возвращаться, чтобы найти изменения в моей деятельности, но, поскольку я хочу, чтобы изменения сохранялись в любом случае, имеет смысл передавать данные через БД.

85 голосов
/ 17 марта 2010

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

  1. Использование Intents
  2. Использование AIDL
  3. Использование самого объекта службы (как синглтона)

В вашем случае я бы выбрал вариант 3. Сделать статическую ссылку на сам сервис и заполнить его в onCreate ():

void onCreate(Intent i) {
  sInstance = this;
}

Сделать статическую функцию MyService getInstance(), которая возвращает статическую sInstance.

Затем в Activity.onCreate() вы запускаете службу, асинхронно ожидаете, пока служба фактически не запустится (ваша служба может уведомить ваше приложение о готовности, отправив намерение в действие.), И получить его экземпляр. Когда у вас есть экземпляр, зарегистрируйте свой объект прослушивателя службы в вашей службе, и вы настроены. ПРИМЕЧАНИЕ: при редактировании представлений внутри Activity вы должны изменить их в потоке пользовательского интерфейса, сервис, вероятно, запустит свой собственный поток, поэтому вам нужно вызвать Activity.runOnUiThread().

Последнее, что вам нужно сделать, - это удалить ссылку на ваш объект прослушивателя в Activity.onPause(), иначе экземпляр вашего контекста активности будет неэффективным.

ПРИМЕЧАНИЕ. Этот метод полезен только в том случае, если ваше приложение / действие / задача является единственным процессом, который будет обращаться к вашему сервису. Если это не так, вы должны использовать вариант 1. или 2.

37 голосов
/ 13 января 2012

Используйте LocalBroadcastManager, чтобы зарегистрировать получателя для прослушивания трансляции, отправленной из локальной службы внутри вашего приложения, ссылка здесь:

http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html

18 голосов
/ 11 марта 2014

Я удивлен, что никто не дал ссылку на библиотеку Otto event Bus

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

Я использую это в своих приложениях для Android, и он работает без проблем.

18 голосов
/ 26 февраля 2012

Использование Messenger - это еще один простой способ связи между Сервисом и Деятельностью.

В упражнении создайте обработчик с соответствующим мессенджером. Это будет обрабатывать сообщения от вашей службы.

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.

8 голосов
/ 04 декабря 2014

Другой метод, который не упомянут в других комментариях, - это привязка к сервису из действия с помощью bindService () и получение экземпляра сервиса в обратном вызове ServiceConnection. Как описано здесь http://developer.android.com/guide/components/bound-services.html

3 голосов
/ 19 ноября 2018

Вы также можете использовать LiveData, который работает как EventBus.

class MyService : LifecycleService() {
    companion object {
        var BUS = MutableLiveData<Object>()
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)

        val testItem : Object

        // expose your data
        if (BUS.hasActiveObservers()) {
            BUS.postValue(testItem)
        }

        return START_NOT_STICKY
    }
}

Затем добавьте наблюдателя из вашего Activity.

MyService.BUS.observe(this, Observer {
    it?.let {
        // Do what you need to do here
    }
})

Вы можете узнать больше из этого блога.

2 голосов
/ 17 августа 2013

Чтобы ответить на ответ @MrSnowflake с примером кода. Это XABBER теперь с открытым исходным кодом Application класс . Класс Application централизует и координирует Listeners, ManagerInterfaces и многое другое. Менеджеры всех видов загружаются динамически. Activity´s запущенный в Xabber сообщит, к какому типу Listener они относятся. И когда Service запускается, он сообщает классу Application как начатый. Теперь, чтобы отправить сообщение на Activity, все, что вам нужно сделать, это сделать Activity стать listener того типа, который вам нужен. В OnStart() OnPause() зарегистрироваться / отменить. Service может запросить у Application класса именно то, с чем ему нужно поговорить, и если он там, то действие готово к приему.

Пройдя через класс Application, вы увидите, что здесь происходит гораздо больше, чем этот.

2 голосов
/ 26 марта 2012

Другим способом может быть использование наблюдателей с поддельным классом модели через действие и сам сервис, реализуя изменение шаблона MVC. Я не знаю, является ли это лучшим способом для достижения этой цели, но это способ, который работал для меня. Если вам нужен пример, попросите об этом, и я что-нибудь опубликую.

1 голос
/ 01 февраля 2019

Мой метод:

Класс для управления отправкой и получением сообщения от / к услуге / деятельности:

import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class MessageManager {

    public interface IOnHandleMessage{
        // Messages
        int MSG_HANDSHAKE = 0x1;

        void onHandleMessage(Message msg);
    }

    private static final String LOGCAT = MessageManager.class.getSimpleName();

    private Messenger mMsgSender;
    private Messenger mMsgReceiver;
    private List<Message> mMessages;

    public MessageManager(IOnHandleMessage callback, IBinder target){
        mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_ACTIVITY));
        mMsgSender = new Messenger(target);
        mMessages = new ArrayList<>();
    }

    public MessageManager(IOnHandleMessage callback){
        mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_SERVICE));
        mMsgSender = null;
        mMessages = new ArrayList<>();
    }

    /* START Getter & Setter Methods */
    public Messenger getMsgSender() {
        return mMsgSender;
    }

    public void setMsgSender(Messenger sender) {
        this.mMsgSender = sender;
    }

    public Messenger getMsgReceiver() {
        return mMsgReceiver;
    }

    public void setMsgReceiver(Messenger receiver) {
        this.mMsgReceiver = receiver;
    }

    public List<Message> getLastMessages() {
        return mMessages;
    }

    public void addMessage(Message message) {
        this.mMessages.add(message);
    }
    /* END Getter & Setter Methods */

    /* START Public Methods */
    public void sendMessage(int what, int arg1, int arg2, Bundle msgData){
        if(mMsgSender != null && mMsgReceiver != null) {
            try {
                Message msg = Message.obtain(null, what, arg1, arg2);
                msg.replyTo = mMsgReceiver;
                if(msgData != null){
                    msg.setData(msgData);
                }
                mMsgSender.send(msg);
            } catch (RemoteException rE) {
                onException(rE);
            }
        }
    }

    public void sendHandshake(){
        if(mMsgSender != null && mMsgReceiver != null){
            sendMessage(IOnHandleMessage.MSG_HANDSHAKE, 0, 0, null);
        }
    }
    /* END Public Methods */

    /* START Private Methods */
    private void onException(Exception e){
        Log.e(LOGCAT, e.getMessage());
        e.printStackTrace();
    }
    /* END Private Methods */

    /** START Private Classes **/
    private class MessageHandler extends Handler {

        // Types
        final static int TYPE_SERVICE = 0x1;
        final static int TYPE_ACTIVITY = 0x2;

        private IOnHandleMessage mCallback;
        private int mType;

        public MessageHandler(IOnHandleMessage callback, int type){
            mCallback = callback;
            mType = type;
        }

        @Override
        public void handleMessage(Message msg){
            addMessage(msg);
            switch(msg.what){
                case IOnHandleMessage.MSG_HANDSHAKE:
                    switch(mType){
                        case TYPE_SERVICE:
                            setMsgSender(msg.replyTo);
                            sendHandshake();
                            break;
                        case TYPE_ACTIVITY:
                            Log.v(LOGCAT, "HERE");
                            break;
                    }
                    break;
                default:
                    if(mCallback != null){
                        mCallback.onHandleMessage(msg);
                    }
                    break;
            }
        }

    }
    /** END Private Classes **/

}

В примере активности:

public class activity extends AppCompatActivity
      implements     ServiceConnection,
                     MessageManager.IOnHandleMessage { 

    [....]

    private MessageManager mMessenger;

    private void initMyMessenger(IBinder iBinder){
        mMessenger = new MessageManager(this, iBinder);
        mMessenger.sendHandshake();
    }

    private void bindToService(){
        Intent intent = new Intent(this, TagScanService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        /* START THE SERVICE IF NEEDED */
    }

    private void unbindToService(){
    /* UNBIND when you want (onDestroy, after operation...)
        if(mBound) {
            unbindService(mServiceConnection);
            mBound = false;
        }
    }

    /* START Override MessageManager.IOnHandleMessage Methods */
    @Override
    public void onHandleMessage(Message msg) {
        switch(msg.what){
            case Constants.MSG_SYNC_PROGRESS:
                Bundle data = msg.getData();
                String text = data.getString(Constants.KEY_MSG_TEXT);
                setMessageProgress(text);
                break;
            case Constants.MSG_START_SYNC:
                onStartSync();
                break;
            case Constants.MSG_END_SYNC:
                onEndSync(msg.arg1 == Constants.ARG1_SUCCESS);
                mBound = false;
                break;
        }
    }
    /* END Override MessageManager.IOnHandleMessage Methods */

    /** START Override ServiceConnection Methods **/
    private class BLEScanServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            initMyMessenger(iBinder);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mMessenger = null;
            mBound = false;
        }
    }
    /** END Override ServiceConnection Methods **/

В службеПример:

public class Blablabla extends Service
    implements     MessageManager.IOnHandleMessage {

    [...]

    private MessageManager mMessenger;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        super.onBind(intent);
        initMessageManager();
        return mMessenger.getMsgReceiver().getBinder();
    }

    private void initMessageManager(){
        mMessenger = new MessageManager(this);
    }

    /* START Override IOnHandleMessage Methods */
    @Override
    public void onHandleMessage(Message msg) {
    /* Do what you want when u get a message looking the "what" attribute */
    }
    /* END Override IOnHandleMessage Methods */

Отправить сообщение от Activity / Service:

mMessenger.sendMessage(what, arg1, arg2, dataBundle);

Как это работает:

на активности, которую вы запускаете или привязываете службу.Методы «OnBind» службы возвращают Binder его MessageManager. В Activity через реализацию методов интерфейса «Service Connection» «OnServiceConnected» вы получаете этот IBinder и инициализируете его с помощью MessageManager.После того, как Activity инициирует свой MessageManager, MessageHandler отправляет и рукопожатие в службу, чтобы он мог установить своего отправителя «MessageHandler» («private Messenger mMsgSender;» в MessageManager).Делая это, служба узнает, кто отправляет его сообщения.

Вы также можете реализовать это с помощью «отправителя» списка / очереди Messenger в MessageManager, чтобы вы могли отправлять несколько сообщений в различные виды деятельности / службы или использоватьсписок / очередь Messenger «получатель» в MessageManager, так что вы можете получать несколько сообщений из разных видов деятельности / услуг.

В экземпляре «MessageManager» у вас есть список всех полученных сообщений.

Как вы можете видеть, соединение между «Activity Messenger» и «Service Messenger» с помощью этого экземпляра «MessageManager» является автоматическим, оно выполняется с помощью метода «OnServiceConnected» и с помощью «Рукопожатия».

Надеюсь, что это полезно для вас :) Большое спасибо!Пока: D

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...