Jak usługa Android komunikować się z aktywnością

Piszę swoją pierwszą aplikację na Androida i staram się skupić na komunikacji między usługami i działaniami. Mam usługę, która będzie działać w tle i zrobić rejestrowanie gps i czasu. Będę miał działanie, które zostanie wykorzystane do uruchomienia i zatrzymania usługi.

Więc najpierw muszę być w stanie dowiedzieć się, czy usługa działa, gdy aktywność jest uruchomiona. Jest kilka innych pytań na ten temat, więc myślę, że mogę to rozgryźć (ale nie krępuj się do udzielania porad).

Mój prawdziwy problem: jeśli aktywność jest uruchomiona i usługa jest uruchomiona, potrzebuję sposobu, aby usługa wysyłała Wiadomości do aktywności. Proste Ciągi i liczby całkowite w tym momencie - głównie wiadomości o statusie. Wiadomości nie będą pojawiać się regularnie, więc nie sądzę, aby sondowanie serwisu było dobrym sposobem, jeśli jest inny sposób. Chcę tę komunikację tylko wtedy, gdy aktywność została rozpoczęta przez użytkownika - nie chcę rozpoczynać aktywności z serwisu. W innych słowa, jeśli rozpoczniesz aktywność i usługa jest uruchomiona, zobaczysz kilka komunikatów o statusie w interfejsie aktywności, gdy wydarzy się coś interesującego. Jeśli nie rozpoczniesz aktywności, nie zobaczysz tych wiadomości(nie są one aż tak interesujące).

Wydaje się, że powinienem być w stanie określić, czy usługa jest uruchomiona, a jeśli tak, dodaj aktywność jako słuchacz. Następnie usuń aktywność jako słuchacza, gdy aktywność zatrzyma się lub zatrzyma. Czy to możliwe? / Align = "left" / aby to zrobić, należy zaimplementować Activity Parcelable i zbudować plik AIDL, abym mógł go przekazać przez Zdalny interfejs usługi. Wydaje się to jednak przesadą i nie mam pojęcia, jak działanie powinno zaimplementować writetoparcel () / readFromParcel ().

Czy jest łatwiejszy czy lepszy sposób? Dzięki za pomoc.

EDIT:

Dla każdego, kto jest zainteresowany tym później, jest przykładowy kod z Google do obsługi tego za pośrednictwem AIDL w katalogu samples: / API/app / RemoteService.java

Author: peterh, 2010-03-17

10 answers

Istnieją trzy oczywiste sposoby komunikowania się z usługami:

  1. Wykorzystanie Intencji
  2. używanie AIDL
  3. używanie samego obiektu service (jako singleton)
W Twoim przypadku wybrałbym opcję 3. Wykonaj statyczne odniesienie do usługi IT self i wypełnij ją w OnCreate ():
void onCreate(Intent i) {
  sInstance = this;
}

Stwórz funkcję statyczną MyService getInstance(), która zwraca statyczną sInstance.

Następnie w Activity.onCreate() uruchamiasz usługę, asynchronicznie czekasz aż usługa zostanie uruchomiona (usługa może powiadomić aplikację, że jest gotowa, wysyłając intencję do aktywności.) i uzyskać jego instancję. Gdy masz instancję, zarejestruj obiekt service listener do usługi i jesteś ustawiony. Uwaga: podczas edycji widoków wewnątrz aktywności należy zmodyfikować je w wątku UI, usługa prawdopodobnie uruchomi własny wątek, więc należy wywołać Activity.runOnUiThread().

Ostatnią rzeczą, którą musisz zrobić, to usunąć odniesienie do obiektu słuchacza w Activity.onPause(), w przeciwnym razie instancja z kontekstu Twojej aktywności będzie wyciekać, nie dobrze.

Uwaga: ta metoda jest przydatna tylko wtedy, gdy aplikacja/aktywność / zadanie jest jedynym procesem, który uzyska dostęp do usługi. Jeśli tak nie jest, musisz użyć opcji 1. lub 2.

 77
Author: MrSnowflake,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-29 08:30:05

Asker prawdopodobnie już dawno o tym zapomniał, ale na wypadek, gdyby ktoś inny tego szukał...

Jest inny sposób na poradzenie sobie z tym, który może być najprostszy.

Dodaj BroadcastReceiver do swojej aktywności. Zarejestruj go, aby otrzymać niestandardowe intencje w onResume i Wyrejestruj go w onPause. Następnie wyślij tę intencję z usługi, gdy chcesz wysłać aktualizacje statusu lub co masz.

Upewnij się, że nie będziesz niezadowolony, jeśli jakaś inna aplikacja słuchał swoich intencji (czy ktoś może zrobić coś złośliwego?), ale poza tym powinno być w porządku.

Poproszono o próbkę kodu:

W mojej służbie mam:

// Do stuff that alters the content of my local SQLite Database
sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT));

(refresh.REFRESH_DATA_INTENT jest tylko ciągiem ciągłym.)

W mojej aktywności odsłuchowej definiuję mój 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
        }
    }
}

Ogłaszam mojego odbiorcę na szczycie klasy:

private DataUpdateReceiver dataUpdateReceiver;

Nadpisuję onResume aby dodać:

if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);

I nadpisuję onPause, aby dodać:

if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);

Teraz moja aktywność polega na słuchaniu mojego serwisu, aby powiedzieć " Hej, idź się zaktualizować."Mogłem przekazać dane w intencji zamiast aktualizować tabele bazy danych, a następnie wrócić, aby znaleźć zmiany w mojej aktywności, ale ponieważ chcę, aby zmiany i tak się utrzymywały, sensowne jest przekazywanie danych przez db.

 239
Author: MaximumGoat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-25 05:02:32

Użyj LocalBroadcastManager aby zarejestrować odbiornik do słuchania transmisji wysyłanej z localservice wewnątrz aplikacji, odniesienie idzie tutaj:

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

 37
Author: Jack Gao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 11:41:11

Dziwię się, że nikt nie podał odwołania do Otto event Bus library

Http://square.github.io/otto/

Używam tego w moich aplikacjach na Androida i działa bezproblemowo.

 18
Author: Madhur Ahuja,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-11 11:22:14

[2]}Używanie komunikatora to kolejny prosty sposób komunikowania się między usługą a działalnością.

W ćwiczeniu Utwórz funkcję obsługi z odpowiednim komunikatorem. Będzie to obsługiwać wiadomości z twojego serwisu.

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());

Posłaniec może być przekazany do służby, dołączając go do wiadomości:

Message message = Message.obtain(null, MyService.ADD_RESPONSE_HANDLER);
message.replyTo = messenger;
try {
    myService.send(message);
catch (RemoteException e) {
    e.printStackTrace();
}

Pełny przykład można znaleźć w demach API: MessengerService i MessengerServiceActivity . Zapoznaj się z pełnym przykładem działania MyService.

 18
Author: Kjetil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-09 22:37:28

Inną metodą, o której nie wspomniano w innych komentarzach, jest powiązanie z usługą z aktywności za pomocą bindService() i uzyskanie instancji usługi w wywołaniu zwrotnym ServiceConnection. Jak opisano tutaj http://developer.android.com/guide/components/bound-services.html

 7
Author: miguel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-04 20:35:20

Innym sposobem może być użycie obserwatorów z fałszywą klasą modelu poprzez działanie i samą usługę, implementując wariację wzorca MVC. Nie wiem, czy to najlepszy sposób, aby to osiągnąć, ale to sposób, który pracował dla mnie. Jeśli potrzebujesz jakiegoś przykładu, poproś o to, a ja coś wpiszę.

 2
Author: ReDirEct__,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-26 13:58:03

Aby kontynuować odpowiedź @ MrSnowflake z przykładem kodu. to jest klasa Xabber teraz open source Application . Klasa Application jest centralizująca i koordynująca Listeners oraz ManagerInterfaces i wiele innych. Wszelkiego rodzaju Menedżery są ładowane dynamicznie. Activity´s rozpoczęte w Xabberze będą raportować, jakiego typu są Listener. A kiedy Service uruchamia raport do Application klasy jako rozpoczętej. Teraz, aby wysłać wiadomość do Activity wystarczy, że Twoja Activity stanie się listener jakiego typu potrzebujesz. W OnStart() OnPause() Zarejestruj się / unreg. {[5] } może poprosić klasę Application o to, że listener musi z nią porozmawiać, a jeśli tam jest, aktywność jest gotowa do odbioru.

Przechodząc przez klasę Application zobaczysz, że jest więcej łupów niż to.

 2
Author: Erik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-22 10:46:45

Jak wspomniał Madhur, możesz używać autobusu do komunikacji.

W przypadku korzystania z magistrali masz kilka opcji:

[[0]}Otto event Bus library (deprecated in for RxJava)

Http://square.github.io/otto/

Green Robot ' s EventBus

Http://greenrobot.org/eventbus/

NYBus (RxBus, zaimplementowany przy użyciu RxJava. bardzo podobny do EventBus)

Https://github.com/MindorksOpenSource/NYBus

 0
Author: Mahpooya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-07 13:41:57

Poza LocalBroadcastManager , Event Bus i Messenger już odpowiedziały na to pytanie,Możemy użyć Pending Intent do komunikacji z serwisu.

Jak wspomniano tutaj w moim wpisie na blogu

Komunikacja pomiędzy usługą a aktywnością może odbywać się za pomocą PendingIntent.Do tego możemy użyć createPendingResult ().createPendingResult () tworzy nowy PendingIntent obiekt, który możesz przekazać do serwisu w celu użycia i wysłania wynik dane do Twojej aktywności w onActivityResult (int, int, Intent).Ponieważ PendingIntent jest Parcelowalny i może dlatego należy umieścić w intencji extra, Twoja aktywność może przekazać to / Align = "left" / Serwis z kolei może zadzwonić wyślij() metoda na PendingIntent powiadamiania o aktywności poprzez na skutek działania zdarzenia.

Aktywność

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);
}
}

Serwis

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);

                }
            }
        }
    }
}
 0
Author: Bhuvnesh Varma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-27 09:08:45