Podstawowe metody komunikacji między serwisem a aktywnością w Androidzie część 1

Im bardziej w las tym więcej drzew. Podobnie jest z aplikacjami na Androida, im bardziej rozbudowana aplikacja tym więcej funkcji. W tym gąszczu funkcji na pewno są serwisy, które wykonują różnorakie rzeczy, np aktualizacja informacji. O ile stworzenie powiadomień nie jest trudne to aktualizacja aktywności może już spowodować pewne problemy. W jaki sposób przekazać dane z serwisu do aktywności dane? Dziś postaram się odpowiedzieć na to pytanie w jaki sposób przekazywać dane. W tej części pokaże Ci podstawowe metody komunikacji między serwisem a aktywnością w androidzie, w drugiej części znajdziesz metody bardziej zaawansowane.

1. Założenia.

Aby lepiej zrozumieć komunikację przedstawię założenia projektu.  Mamy serwis, który co sekundę zwiększa liczbę o 1. Nasz serwis może wyglądać tak:

public class LocalService extends Service {
    private boolean mStartCounting = false;
    private int mCounter  = 0;
    private Context mContext;
    @Override
    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
        if (!mStartCounting)
            startCounting();
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
    private void startCounting() {
        mStartCounting = true;
        new Thread(new Runnable(){
            public void run() {
                while (mStartCounting) {
                    SystemClock.sleep(1000);
                    mCounter++;
                }
            }
        }).start();
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

Teraz musimy przesłać mConuter z serwisu do aktywności. Natomiast z aktywności zatrzymujemy liczenie.

2. Skorzystaj z Intenta.

Najprostsza forma przekazywania danych między aktywnościami, ale także między service a activity to skorzystanie z Intenta. Aby wysłać informacje o zatrzymaniu liczenia, z aktywności wykonujemy taki kod:

Intent sendIntent = new Intent(MainActivity.this,LocalService.class);
sendIntent.putExtra("startCounting", false);
startService(sendIntent);

Aby odebrać dane z intencji w serwisie to w metodzie onStartCommand() dodajemy:

if (intent.getExtras() != null) {
    mStartCounting = intent.getBooleanExtra("startCounting", false);
}

Jeszcze pozostało nam poinformowanie aktywności o liczbie. W serwisie w pętli while dodajemy:

Intent intent = new Intent(mContext, MainActivity.class);
intent.putExtra("mCounter",mCounter);
startActivity(intent);

Zaś w aktywności odbieramy intencję w ten sposób:

if (getIntent().getExtras() != null) {
    int mCounter = getIntent().getIntExtra("mCounter",0);
    Log.d("TAG", "Activity: "+mCounter );
}

Użyj tej metody, jeśli przekazujesz prymitywne dane. Możesz także przekazywać obiekty, które implementuje Serializable. Serializable niestety jest wolny dlatego lepszym rozwiązaniem jest skorzystanie z Parcelable (sporo kodu do pisania). Kolejną wadą tej metody jest to, że aktywność będzie tworzona na nowo lub wznawiana w zależności od cyklu życia aktywności. Tą metodę warto stosować gdy faktycznie musimy wyświetlić ekran po zakończeniu działania jakiejś czynności. No chyba, że preferujesz lepszą praktykę i skorzystasz z powiadomień. Jeżeli masz bardziej złożone obiekty i chcesz jednak skorzystać z intencji to użyj gson, aby przekształcić obiekt w JSON.

3. Statyczna metoda, zmienna.

Innym sposobem poinformowania serwisu o zaprzestaniu liczenia jest stworzenie statycznej metody lub zmiennej. W aktywności wywołujemy taki kod:

LocalService.setCounting(false);

A w serwisie tworzymy taką metodę:

public static void setCounting(boolean counting){
    mStartCounting = counting;
}

Wtedy naszą zmienną musimy zmienić na statyczną:

private static boolean mStartCounting = false;

Nic nie stoi na przeszkodzie abyśmy zrobili ją publiczną i zmieniali wartość bezpośrednio w aktywności.
Minus tego rozwiązania będzie tworzenie geterów i seterów – kiepskie rozwiązanie. Na pewno ten sposób nie jest bezpieczny, ale już lepiej ponieważ aktywność nie jest wielokrotnie „przetwarzana”.
Oczywiście też możesz wysłać instancję aktywności przez Intent i odwołać się do metod prywatnych, ale czy to ma sens? Wątpię, są o wiele lepsze metody.

4. Podłącz się do serwisu.

Najczęstszym sposobem i też bezpiecznym jest podłączenie się do usługi. Jeśli Twoja usługa jest używana przez lokalną aplikację i nie musi pracować pomiędzy procesami, możesz zaimplementować własną klasę Binder, która zapewnia klientowi (aktywności) bezpośredni dostęp do publicznych metod w usłudze. Dodajmy do naszej usługi taki kod:

private final IBinder mBinder = new LocalBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}
public class LocalBinder extends Binder {
    LocalService getService() {
        return LocalService.this;
    }
}
public void setCounting(boolean counting){
    mStartCounting = counting;
}

Binder wykonuje jedną z następujących czynności:

  • Zawiera publiczne metody, które może wywoływać klient,
  • Zwraca bieżącą instancję usługi, która ma publiczne metody i może je wywołać klient,
  • Zwraca instancję innej klasy hostowanej przez usługę i posiada również publiczne metody, które mogą być wywołane przez klienta.

Teraz zobacz jak zastosować to w klasie z aktywnością:

private LocalService mService;
private boolean mBound = false;
private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
        mService = null;
        mBound = false;
    }
};
@Override
protected void onStop() {
    super.onStop();
    if (mBound) {
        unbindService(mConnection);
        mBound = false;
    }
}

Aby podłączyć się do usługi i wywołać zatrzymanie liczenia robisz tak:

bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
mService.setCounting(false);

Podłączając się do usługi, metoda getService() zwraca instancję usługi. Jak już mamy taki obiekt możemy wywoływać publiczne metody, które znajdują się w usłudze. Kod powyższy pozwala Ci sterować z poziomu aktywności usługą. Natomiast jeśli chcesz uzyskać informację od serwisu o liczbie możesz zastosować implementacje interfejsu. W usłudze dodaj taki kod:

private ServiceCallbacks serviceCallbacks;
public interface ServiceCallbacks{
    void showNumber(int number);
}

W klasie LocalBinder dodaj metodę:

public void setCallbacks(ServiceCallbacks callbacks) {
    serviceCallbacks = callbacks;
}

Teraz w aktywności w metodzie onServiceConnected() pobierz callback’a:

binder.setCallbacks(serviceCallbacks);

I jeszcze implementacja:

private LocalService.ServiceCallbacks serviceCallbacks = new LocalService.ServiceCallbacks() {
    @Override
    public void showNumber(int number) {
        Log.d("TAG", "Activity: "+number );
    }
};

Ok, masz teraz komunikację serwisem a aktywnością i odwrotnie. Pamiętaj, że usługa i klient muszą znajdować się w tej samej aplikacji.

5. BroadcastReceiver.

Jeszcze innym sposobem na komunikację miedzy serwisem a aktywnością jest BroadcastReceiverTa metoda przydaje się gdy chcemy nadsłuchiwać zdarzeń w systemie, a serwis te zdarzenia przetwarza na przykład sprawdza wiadomości gdy internet jest dostępny. Mamy dwa rodzaje rejestracji receivera: statycznie przez plik manifestu oraz dynamicznie w kodzie. Poniżej zobaczysz przykład broadcastreceiver zarejestrowanego dynamicznie. Będzie działać tylko wtedy gdy aplikacja będzie działać. W usłudze dodajemy:

final String mBroadcastStringAction = "net.myenv.broadcast.mStartCounting";
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(mBroadcastStringAction)) {
           mStartCounting = false;
        }
    }
};
private void setupReceiver() {
    IntentFilter mIntentFilter = new IntentFilter();
    mIntentFilter.addAction(mBroadcastStringAction);
    registerReceiver(mReceiver,mIntentFilter);
}

W metodzie onCreate() wywołaj setupReceiver(). Jeżeli chcesz wyrejestrować to wywołujesz:

unregisterReceiver(mReceiver);

Natomiast w aktywności wywołujesz zatrzymanie liczenia w ten sposób:

final String mBroadcastStringAction = "net.myenv.broadcast.mStartCounting";
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(mBroadcastStringAction);
broadcastIntent.putExtra("startCounting", false);
sendBroadcast(broadcastIntent);

Szybkie i proste, prawda? Masz teraz komunikację aktywność –> serwis. Aby z serwisu pobierać dane robisz podobnie tylko w drugą stronę. Ze względu na pewne ograniczenia wprowadzone w Android O zalecam korzystanie z dynamicznych broadcasterów.

6. Inne Metody.

Oprócz wyżej wymienionych metod możesz jeszcze skorzystać z takich opcji jak:

O ile powyższe metody są ok, to ich zastosowanie jest nie zawsze poprawne. Ponieważ w aktywności i serwisie musiałbyś zaimplementować odczytywanie tych wartości co jakiś określony czas, w powyższym przypadku co sekundę. Więc te metody nie zawsze mogą się sprawdzić pod względem wydajności. Pamiętaj o tym też, że system Android może unicestwić aktywnoś

7. Podsumowanie.

To nie koniec metod komunikacji między serwisem a aktywnością w androidzie. Jest jeszcze sporo innych, tutaj ogranicza nas tylko wyobraźnia. Gdybym chciał Ci przedstawić wszystkie wpis byłby ogromy, dlatego uznałem, że podzielę to zagadnienie na dwie części. W tej części przedstawiłem tylko podstawowe i najczęściej spotykane metody. W kolejnej części poznasz bardziej zaawansowane metody.
Pamiętaj, że to od Ciebie i Twojego projektu zależy, który sposób wykorzystasz. Nie ma jednej uniwersalnej metody komunikacji między serwisem a aktywnością w androidzie.

Co dalej?

  • Polub stronę MYENV na Facebooku oraz śledź mnie na Twitterze
  • Zachęcam do komentowania i pisania propozycji tematów, o których chcesz przeczytać
  • Poleć ten wpis za pomocą poniższych przycisków. Będę Ci za to bardzo wdzięczny 🙂
  • Życzę Ci miłego dnia i miłego kodowania 🙂