Broadcast Receiver w Androidzie – co to takiego?
W dzisiejszym artykule zapoznamy się z tak zwanym BroadcastReceiverem
zaimplementowanym w Anroidzie. Jest to mechanizm, który umożliwia komunikację między systemem, a komponentami naszej aplikacji. Rozwiązanie to wykorzystywane jest również, do wewnętrznej komunikacji w aplikacji oraz całym systemie (można je wykorzystać na przykład do przekazywania danych między dwoma różnymi programami). Pewnie brzmi skomplikowanie? Ale na szczęście tak nie jest, zobaczycie zresztą sami…
BroadcastReceiver – implementacja
BroadcastReceivera w aplikacji możemy zaimplementować na dwa sposoby. Jako oddzielną klasę rozszerzającą BroadcastReceiver
(wcześniej rejestrując ją w manifeście), na przykład w ten sposób:
public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub } }
lub jako klasę zagnieżdżoną (przykładowo umieszczając kod w metodzie):
public void someMethod() { BroadcastReceiver myReciver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { // BroadcastReciver } }; }
UWAGA: Klasę zagnieżdżoną z BradcastReceiver możemy zaimplementować tylko i wyłącznie w komponencie (np. aktywności).
Na czym będziemy pracować?
Ok, wiemy już jak zaimplementować BroadcastReceivera w projekcie, teraz pora przejść do jakiegoś praktycznego przykładu. Jak zwykle aby nie tracić czasu utworzymy sobie w Android Studio pusty projekt, na którym będziemy dalej pracować (tworzenie projektów opisywałem w tym artykule). Oczywiście przyda nam się również kontrolka umożliwiająca wyświetlanie tekstu (np. TextView
) wstawiamy więc ją na środku głównego ekranu aplikacji.
Wersja dla leniwych jest do pobrania tutaj (projekt wykonany w Android Studio 2.2).
Problem…
Chcemy wkorzystać BroadcastRecivera do rozwiązania jakiegoś problemu, załóżmy więc, że potrzebujemy aby w naszej aplikacji wyświetlała się informacja o podpięciu telefonu do ładowarki (rozpoczęciu ładowania) i odłączeniu telefonu od źródła zasilania (zakończenie ładowania), a kiedy telefon jest podpięty do ładowarki chcemy aby program wyświetlał odpowiedni komunikat na ekranie.
Rejestracja BroadcastReceivera w manifeście
Pierwszym krokiem od jakiego zaczniemy jest rejestracja Receivera. Należy to zrobić w manifeście. Po otwarciu naszego projektu w Android Studio przechodzimy więc do pliku AndroidManifest.xml
znajdującego się w katalogu manifest
i otwieramy go.
Następnie wewnątrz tagu <applcation ...> ... </application>
wstawiamy taki tekst:
<receiver android:name=".MyReceiver"> <intent-filter> </intent-filter> </receiver>
Jak widać, zrobiłem przerwę w 4 i 5 linijce, nie bez powodu. W to miejsce wstawimy ten kod:
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" /> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
Umożliwia on nam dostęp do informacji o zdarzeniu „podłączenia ładowarki” (linijka 1) oraz „odłączenia ładowarki” (linijka 2).
Całość prezentuje się następująco:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="strefakoderapl.broadcastreceiverexample"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <receiver android:name=".MyReciver" > <intent-filter > <action android:name="android.intent.action.ACTION_POWER_CONNECTED" /> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" /> </intent-filter> </receiver> <activity android:name="strefakoderapl.broadcastreceiverexample.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
W 12 linijce powyższego kodu IDE w którym pracujemy powinno poinformować nas o błędzie. Chodzi między innymi o to, że odwołujemy się tutaj do klasy MyReciver
, która nie istnieje.
Tworzymy więc odpowiednią klasę w projekcie:
import android.content.BroadcastReceiver; public class MyReciver extends BroadcastReceiver { }
i implementujemy metodę onReceive()
:
public class MyReciver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { } }
Obsługa zdarzeń przez BroadcastReceivera
W metodzie onReceive()
zrobimy obsługę naszych zdarzeń. Chcemy aby na ekranie wyświetlał się komunikat informujący nas o tym, że podłączyliśmy telefon do ładowarki. Korzystamy więc z obiektu intent
(który otrzymujemy jako argument) i wywołujemy na nim metodę getActions()
, kolejnym elementem jest sprawdzenie czy jest to zdarzenie, które chcemy obsłużyć czyli android.intent.action.ACTION_POWER_CONNECTED
(podłączenie telefonu do źródła zasilania), robimy to za pomocą wbudowanej w Jave metody equals()
:
@Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals("android.intent.action.ACTION_POWER_CONNECTED")) { //obsługa zdarzenia podpięcia telefonu do ładowarki } }
Ok, teraz pozostało nam tylko wyświetlić odpowiedni komunikat:
public class MyReciver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals("android.intent.action.ACTION_POWER_CONNECTED")) Toast.makeText(context, "Telefon został podłączony do ładowarki", Toast.LENGTH_LONG).show(); } }
Nasz program już praktycznie działa. Możemy teraz uruchomić aplikację na telefonie, włączyć ją i podłączyć telefon do ładowarki, a otrzymamy stosowną informację o tym wydarzeniu.
UWAGA: Kiedy zamkniemy aplikację (ale dalej będzie ona zainstalowana w systemie) i podłączymy telefon do źródła zasilania, również zostaniemy o tym poinformowani odpowiednim komunikatem.
UWAGA: BroadcastReceiver zarejestrowany w Manifeście działa niezależnie od tego, czy aplikacja jest uruchomiona czy nie.
Analogicznie dodajemy obsługę zdarzenia informującego o odłączeniu ładowarki:
@Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals("android.intent.action.ACTION_POWER_CONNECTED")) Toast.makeText(context, "Telefon został podłączony do ładowarki", Toast.LENGTH_LONG).show(); if(intent.getAction().equals("android.intent.action.ACTION_POWER_DISCONNECTED")) Toast.makeText(context, "Telefon został odłączony od ładowarki", Toast.LENGTH_LONG).show(); }
MyReciver
, który stworzyliśmy wyświetla tylko komunikaty informujące o podłączeniu ładowarki bądź jej odłączeniu. Nie możemy za jego pomocą wyświetlić w aplikacji stałej informacji mówiącej o tym, że telefon jest właśnie w trakcie ładowania. Dzieje się tak dla tego, że BroadcastReceiver został zarejestrowany w manifeście, a więc będzie działał nawet jak nasz program jest obecnie wyłączony. Gdybyśmy teraz chcieli w jakiejś kontrolce z MainActivity
wyświetlić tekst doszło by do błędu.
BroadcastReceiver w Aktywności
Naszym celem jest jednak wyświetlanie stałej informacji o tym, że telefon jest w trakcie ładowania. Aby to zrobić musimy skorzystać z drugiej implementacji BroadcastReceivera
czyli zarejestrować go w Aktywności. Wtedy taki BroadcastReceiver
działa tylko jeżeli dana aktywność jest aktywna.
Jak to zrobić? Pierwszym krokiem będzie umieszczenie klasy zagnieżdżonej BroadcastReceiver
w metodzie onCreate
wybranej aktywności (zgodnie z przykładem na początku artykułu):
BroadcastReceiver myReceiver1 = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { //obsługa zdarzeń podpięcia telefonu do ładowarki bądź odłączenia telefonu od ładowarki } };
Aby nasz BroadcastReceiver
był obsługiwany musimy go zarejestrować oraz „poinformować” system, jakie zdarzenie ma obsługiwać:
registerReceiver(myReceiver1, new IntentFilter(Intent.ACTION_POWER_CONNECTED));
Jak widać, użyliśmy do tego metody registerReceiver()
gdzie jako pierwszy argument podaliśmy obiekt reprezentujący nasz BroadcastReceiver
, a jako drugi argument podaliśmy obiekt IntentFilter
z informacją, że zdarzenie które chcemy obsłużyć to ACTION_POWER_CONNECTED
(podłączenie telefonu do ładowarki).
Pozostało tylko dopisać obsługę komunikatu:
final TextView textView = (TextView) findViewById(R.id.textView); BroadcastReceiver myReceiver1 = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { textView.setText("Telefon jest w trakcie ładowania..."); } };
UWAGA: Obiekt textView
używamy teraz w innej klasie, aby więc wszystko działało przy jego inicjalizacji musimy dopisać słowo kluczowe final
.
Analogicznie robimy drugiego BroadcastReceivera
obsługującego zdarzenie odłączenia ładowarki:
BroadcastReceiver myReceiver2 = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { textView.setText("Telefon nie jest podłączony do ładowarki..."); } }; registerReceiver(myReceiver2, new IntentFilter(Intent.ACTION_POWER_DISCONNECTED));
Cały kod metody onCreate()
:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(strefakoderapl.broadcastreceiverexample.R.layout.activity_main); final TextView textView = (TextView) findViewById(R.id.textView); BroadcastReceiver myReceiver1 = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { textView.setText("Telefon jest w trakcie ładowania..."); } }; BroadcastReceiver myReceiver2 = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { textView.setText("Telefon nie jest podłączony do ładowarki..."); } }; registerReceiver(myReceiver1, new IntentFilter(Intent.ACTION_POWER_CONNECTED)); registerReceiver(myReceiver2, new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)); }
Nasz program jest już gotowy możemy go uruchomić i zobaczyć jak działa.
Ukończony projekt do pobrania tutaj.
Dzięki, szybko, łatwo i przejrzyście wytłumaczone.
Super opisne, ale jak zrobić aby w textView wyświetlać status otrzymania SMS-a, czyli zamiast ACTION_POWER_CONNECTED wybrać SMS_RECEIVED???
Niestety sama podmiana akcji nie pomaga.
Super opisne, ale jak zrobić aby w textView wyświetlać status otrzymania SMS-a, czyli zamiast ACTION_POWER_CONNECTED wybrać SMS_RECEIVED???