Hôm nay, chúng ta sẽ cùng nhau đi sâu vào một trong những thành phần cốt lõi và mạnh mẽ nhất của Android Framework: BroadcastReceiver. Đây là một component cho phép ứng dụng của chúng ta lắng nghe và phản ứng với các sự kiện hoặc Intent
được phát đi trên toàn hệ thống hoặc từ các ứng dụng khác.
Android BroadcastReceiver là gì?
Như đã đề cập, Android BroadcastReceiver
là một thành phần “ngủ đông” (dormant component) của Android, có khả năng lắng nghe các sự kiện hoặc Intent
được phát sóng trên toàn hệ thống. Khi bất kỳ sự kiện nào trong số này xảy ra, nó sẽ kích hoạt ứng dụng của chúng ta bằng cách tạo ra một thông báo trên thanh trạng thái hoặc thực hiện một tác vụ cụ thể. BroadcastReceiver
thường được triển khai để ủy quyền các tác vụ cho Service
tùy thuộc vào loại dữ liệu Intent
mà nó nhận được. Dưới đây là một số Intent
quan trọng do hệ thống tạo ra:
android.intent.action.BATTERY_LOW
: Báo hiệu tình trạng pin yếu của thiết bị.android.intent.action.BOOT_COMPLETED
: Được báo hiệu một lần duy nhất sau khi hệ thống hoàn tất quá trình khởi động.android.intent.action.CALL
: Thực hiện cuộc gọi đến số điện thoại được chỉ định trong dữ liệu.android.intent.action.DATE_CHANGED
: Ngày trên thiết bị đã thay đổi.android.intent.action.REBOOT
: Yêu cầu thiết bị khởi động lại.android.net.conn.CONNECTIVITY_CHANGE
: Trạng thái kết nối mạng di động hoặc Wi-Fi đã thay đổi (hoặc được thiết lập lại).
Thiết lập Broadcast Receiver trong Android
Để thiết lập một BroadcastReceiver
trong ứng dụng Android, chúng ta cần thực hiện hai bước chính sau đây:
- Tạo một
BroadcastReceiver
- Đăng ký một
BroadcastReceiver
Tạo một BroadcastReceiver
Chúng ta hãy nhanh chóng triển khai một BroadcastReceiver
tùy chỉnh như ví dụ dưới đây:
public class MyReceiver extends BroadcastReceiver {
public MyReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Action: " + intent.getAction(), Toast.LENGTH_SHORT).show();
}
}
BroadcastReceiver
là một lớp trừu tượng (abstract class), và phương thức onReceive()
của nó là trừu tượng. Hệ thống sẽ gọi phương thức onReceive()
này đầu tiên trên các BroadcastReceiver
đã đăng ký khi bất kỳ sự kiện nào xảy ra. Đối tượng Intent
được truyền vào mang theo tất cả dữ liệu bổ sung liên quan đến sự kiện. Một đối tượng Context
cũng có sẵn và chúng ta sử dụng nó để khởi động một Activity
hoặc Service
tương ứng bằng cách gọi context.startActivity(myIntent);
hoặc context.startService(myService);
.
Đăng ký BroadcastReceiver trong ứng dụng Android
Chúng ta có thể đăng ký một BroadcastReceiver
theo hai cách.
- Bằng cách định nghĩa trong tệp
AndroidManifest.xml
như hình dưới đây:<receiver android:name=".ConnectionReceiver" > <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver>
Sử dụng
intent-filter
, chúng ta thông báo cho hệ thống rằng bất kỳIntent
nào khớp với các thẻ con của chúng ta (ví dụ:<action>
) đều phải được gửi đếnBroadcastReceiver
cụ thể đó. - Bằng cách định nghĩa theo chương trình (programmatically):Đoạn mã sau đây hiển thị một ví dụ mẫu để đăng ký
BroadcastReceiver
theo chương trình:IntentFilter filter = new IntentFilter(); intentFilter.addAction(getPackageName() + "android.net.conn.CONNECTIVITY_CHANGE"); MyReceiver myReceiver = new MyReceiver(); registerReceiver(myReceiver, filter);
Để hủy đăng ký một
BroadcastReceiver
trong phương thứconStop()
hoặconPause()
củaActivity
, bạn có thể sử dụng đoạn mã sau:@Override protected void onPause() { unregisterReceiver(myReceiver); super.onPause(); }
Gửi Broadcast intents từ Activity
Chúng ta sử dụng đoạn mã sau để gửi một Intent
đến tất cả các BroadcastReceiver
liên quan:
Intent intent = new Intent();
intent.setAction("com.journaldev.CUSTOM_INTENT");
sendBroadcast(intent);
Đừng quên thêm action này vào thẻ intent-filter
trong Manifest
hoặc đăng ký nó theo chương trình!
Chúng ta hãy cùng phát triển một ứng dụng lắng nghe các sự kiện thay đổi mạng và cả một Intent
tùy chỉnh, sau đó xử lý dữ liệu tương ứng.
Cấu trúc dự án Android với BroadcastReceiver
Để minh họa rõ hơn cách các thành phần này hoạt động cùng nhau, chúng ta sẽ xem xét cấu trúc dự án và mã nguồn cụ thể.
Mã nguồn Android BroadcastReceiver
Tệp activity_main.xml
chứa một nút (Button
) ở giữa màn hình dùng để gửi Intent
phát sóng.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="<https://schemas.android.com/apk/res/android>"
xmlns:tools="<https://schemas.android.com/tools>"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.journaldev.broadcastreceiver.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button"
android:text="Send Broadcast"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
Tệp MainActivity.java
được cung cấp dưới đây:
package com.journaldev.broadcastreceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
ConnectionReceiver receiver;
IntentFilter intentFilter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
receiver = new ConnectionReceiver();
intentFilter = new IntentFilter("com.journaldev.broadcastreceiver.SOME_ACTION");
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(receiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
@OnClick(R.id.button)
void someMethod() {
Intent intent = new Intent("com.journaldev.broadcastreceiver.SOME_ACTION");
sendBroadcast(intent);
}
}
Trong đoạn mã trên, chúng ta đã đăng ký một action tùy chỉnh khác theo chương trình. ConnectionReceiver
được định nghĩa trong tệp AndroidManifest.xml
như sau:
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="<https://schemas.android.com/apk/res/android>"
package="com.journaldev.broadcastreceiver">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".ConnectionReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
</application>
</manifest>
Lớp ConnectionReceiver.java
được định nghĩa dưới đây:
public class ConnectionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("API123",""+intent.getAction());
if(intent.getAction().equals("com.journaldev.broadcastreceiver.SOME_ACTION"))
Toast.makeText(context, "SOME_ACTION is received", Toast.LENGTH_LONG).show();
else {
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
activeNetwork.isConnectedOrConnecting();
if (isConnected) {
try {
Toast.makeText(context, "Network is connected", Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
}
} else {
Toast.makeText(context, "Network is changed or reconnected", Toast.LENGTH_LONG).show();
}
}
}
}
Trong đoạn mã trên, chúng ta kiểm tra action
của Intent
kích hoạt phương thức onReceive()
và dựa vào đó để hiển thị Toast
.
Lưu ý quan trọng: Để BroadcastReceiver
của bạn không khả dụng cho các ứng dụng bên ngoài, bạn hãy thêm thuộc tính android:exported=false
vào thẻ <receiver>
trong Manifest
. Khi chúng ta gửi một broadcast
, các ứng dụng bên ngoài cũng có khả năng nhận chúng. Chúng ta có thể ngăn chặn điều này bằng cách chỉ định hạn chế này.
Output của ứng dụng khi chạy sẽ hiển thị các thông báo Toast
tương ứng với các sự kiện.
Kết luận
BroadcastReceiver
thực sự là một thành phần không thể thiếu trong phát triển Android, cho phép ứng dụng của chúng ta phản ứng một cách thông minh và hiệu quả với các sự kiện hệ thống hoặc tùy chỉnh. Nắm vững BroadcastReceiver
giúp bạn xây dựng các ứng dụng linh hoạt, có khả năng phản ứng trong thời gian thực với các thay đổi môi trường của thiết bị.