Các ứng dụng Android gửi và nhận thông báo truyền tin từ hệ thống Android và các ứng dụng Android khác, tương tự như mẫu thiết kế xuất bản-đăng ký. Hệ thống và các ứng dụng thường gửi thông báo truyền tin khi một số sự kiện nhất định xảy ra. Ví dụ: hệ thống Android sẽ gửi thông báo truyền tin khi nhiều sự kiện hệ thống xảy ra, chẳng hạn như khi hệ thống khởi động hoặc thiết bị sạc. Các ứng dụng cũng gửi thông báo truyền tin tuỳ chỉnh, chẳng hạn như để thông báo cho các ứng dụng khác về một nội dung mà ứng dụng đó có thể quan tâm (ví dụ: tải dữ liệu mới xuống).
Các ứng dụng có thể đăng ký nhận các thông báo cụ thể. Khi một thông báo truyền tin được gửi, hệ thống sẽ tự động định tuyến thông báo truyền tin đến những ứng dụng đã đăng ký nhận loại thông báo truyền tin cụ thể đó.
Nói chung, thông báo truyền tin có thể được dùng làm hệ thống nhắn tin trên các ứng dụng và bên ngoài luồng người dùng thông thường. Tuy nhiên, bạn phải cẩn thận để không lạm dụng cơ hội phản hồi các thông báo truyền tin và chạy các tác vụ ở chế độ nền có thể góp phần làm giảm hiệu suất hệ thống.
Giới thiệu về thông báo truyền phát của hệ thống
Hệ thống sẽ tự động gửi thông báo truyền tin khi các sự kiện hệ thống khác nhau xảy ra, chẳng hạn như khi hệ thống chuyển đổi giữa chế độ trên máy bay và chế độ thông thường. Tất cả các ứng dụng đã đăng ký đều nhận được thông báo phát sóng này.
Đối tượng Intent bao bọc thông báo phát đi. Chuỗi action xác định sự kiện đã xảy ra, chẳng hạn như android.intent.action.AIRPLANE_MODE. Ý định cũng có thể bao gồm thông tin bổ sung được gói vào trường bổ sung của ý định.
Ví dụ: ý định Chế độ trên máy bay có thêm một giá trị boolean cho biết Chế độ trên máy bay đang bật hay tắt.
Để biết thêm thông tin về cách đọc ý định và lấy chuỗi hành động từ một ý định, hãy xem bài viết Ý định và bộ lọc ý định.
Tin do hệ thống truyền ra
Để xem danh sách đầy đủ các thao tác tin do hệ thống truyền ra, hãy xem tệp BROADCAST_ACTIONS.TXT trong SDK Android. Mỗi thao tác truyền tin đều có một trường hằng số được liên kết với thao tác đó. Ví dụ: giá trị của hằng số ACTION_AIRPLANE_MODE_CHANGED là android.intent.action.AIRPLANE_MODE.
Tài liệu cho từng thao tác truyền tin có trong trường hằng số được liên kết.
Thay đổi đối với thông báo hệ thống
Khi nền tảng Android phát triển, nền tảng này sẽ định kỳ thay đổi cách hoạt động của các thông báo hệ thống. Hãy lưu ý những thay đổi sau để hỗ trợ tất cả các phiên bản Android.
Android 16
Trong Android 16, thứ tự phân phối thông báo truyền tin bằng cách sử dụng thuộc tính android:priority hoặc IntentFilter.setPriority() trên các quy trình khác nhau sẽ không được đảm bảo. Mức độ ưu tiên của thông báo truyền tin chỉ được tuân thủ trong cùng một quy trình ứng dụng thay vì trên tất cả các quy trình.
Ngoài ra, các mức độ ưu tiên phát sóng sẽ tự động bị giới hạn trong phạm vi (SYSTEM_LOW_PRIORITY + 1, SYSTEM_HIGH_PRIORITY – 1).
Chỉ các thành phần hệ thống mới được phép đặt SYSTEM_LOW_PRIORITY, SYSTEM_HIGH_PRIORITY làm mức độ ưu tiên truyền tin.
Android 14
Trong khi các ứng dụng ở trạng thái lưu vào bộ nhớ đệm, hệ thống sẽ tối ưu hoá việc phân phối tin truyền để đảm bảo tình trạng hệ thống. Ví dụ: hệ thống sẽ trì hoãn các tin truyền hệ thống ít quan trọng hơn (chẳng hạn như ACTION_SCREEN_ON) trong khi ứng dụng ở trạng thái đã lưu vào bộ nhớ đệm.
Khi ứng dụng chuyển từ trạng thái đã lưu vào bộ nhớ đệm sang vòng đời quy trình đang hoạt động, hệ thống sẽ phân phối mọi tin truyền bị hoãn lại.
Các tin truyền quan trọng được khai báo trong tệp kê khai sẽ tạm thời xoá các ứng dụng khỏi trạng thái đã lưu vào bộ nhớ đệm để phân phối.
Android 9
Bắt đầu từ Android 9 (cấp độ API 28), thông báo truyền tin NETWORK_STATE_CHANGED_ACTION sẽ không nhận được thông tin về vị trí của người dùng hoặc dữ liệu nhận dạng cá nhân.
Nếu ứng dụng của bạn được cài đặt trên một thiết bị chạy Android 9.0 (cấp độ API 28) trở lên, thì hệ thống sẽ không đưa SSID, BSSID, thông tin kết nối hoặc kết quả quét vào các thông báo Wi-Fi. Để nhận thông tin này, hãy gọi getConnectionInfo().
Android 8.0
Kể từ Android 8.0 (cấp độ API 26), hệ thống sẽ áp đặt các hạn chế bổ sung đối với các receiver được khai báo trong tệp kê khai.
Nếu ứng dụng của bạn nhắm đến Android 8.0 trở lên, bạn không thể dùng tệp kê khai để khai báo một bộ nhận cho hầu hết các thông báo truyền tin ngầm (những thông báo truyền tin không nhắm đến riêng ứng dụng của bạn). Bạn vẫn có thể sử dụng receiver đã đăng ký theo bối cảnh khi người dùng đang tích cực sử dụng ứng dụng của bạn.
Android 7.0
Android 7.0 (cấp độ API 24) trở lên không gửi các tin do hệ thống truyền ra sau:
Ngoài ra, các ứng dụng nhắm đến Android 7.0 trở lên phải đăng ký thông báo CONNECTIVITY_ACTION bằng cách sử dụng registerReceiver(BroadcastReceiver, IntentFilter). Việc khai báo một receiver trong tệp kê khai không hoạt động.
Nhận nội dung truyền phát
Ứng dụng có thể nhận thông báo theo hai cách: thông qua các receiver đã đăng ký theo bối cảnh và các receiver được khai báo trong tệp kê khai.
Bộ nhận đã đăng ký theo bối cảnh
Bộ nhận đã đăng ký theo bối cảnh sẽ nhận được thông báo truyền tin miễn là bối cảnh đăng ký của chúng còn hợp lệ. Thao tác này thường nằm giữa các lệnh gọi đến registerReceiver và unregisterReceiver. Ngữ cảnh đăng ký cũng trở nên không hợp lệ khi hệ thống huỷ ngữ cảnh tương ứng. Ví dụ: nếu đăng ký trong ngữ cảnh Activity, bạn sẽ nhận được thông báo truyền tin miễn là hoạt động vẫn đang diễn ra. Nếu đăng ký bằng ngữ cảnh Ứng dụng, bạn sẽ nhận được thông báo truyền tin miễn là ứng dụng chạy.
Để đăng ký một receiver bằng ngữ cảnh, hãy thực hiện các bước sau:
Trong tệp bản dựng cấp mô-đun của ứng dụng, hãy thêm phiên bản 1.9.0 trở lên của thư viện AndroidX Core:
Groovy
dependencies { def core_version = "1.18.0" // Java language implementation implementation "androidx.core:core:$core_version" // Kotlin implementation "androidx.core:core-ktx:$core_version" // To use RoleManagerCompat implementation "androidx.core:core-role:1.1.0" // To use the Animator APIs implementation "androidx.core:core-animation:1.0.0" // To test the Animator APIs androidTestImplementation "androidx.core:core-animation-testing:1.0.0" // Optional - To enable APIs that query the performance characteristics of GMS devices. implementation "androidx.core:core-performance:1.0.0" // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google implementation "androidx.core:core-google-shortcuts:1.1.0" // Optional - to support backwards compatibility of RemoteViews implementation "androidx.core:core-remoteviews:1.1.0" // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12 implementation "androidx.core:core-splashscreen:1.2.0" }
Kotlin
dependencies { val core_version = "1.18.0" // Java language implementation implementation("androidx.core:core:$core_version") // Kotlin implementation("androidx.core:core-ktx:$core_version") // To use RoleManagerCompat implementation("androidx.core:core-role:1.1.0") // To use the Animator APIs implementation("androidx.core:core-animation:1.0.0") // To test the Animator APIs androidTestImplementation("androidx.core:core-animation-testing:1.0.0") // Optional - To enable APIs that query the performance characteristics of GMS devices. implementation("androidx.core:core-performance:1.0.0") // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google implementation("androidx.core:core-google-shortcuts:1.1.0") // Optional - to support backwards compatibility of RemoteViews implementation("androidx.core:core-remoteviews:1.1.0") // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12 implementation("androidx.core:core-splashscreen:1.2.0") }
Tạo một thực thể của
BroadcastReceiver:Kotlin
val myBroadcastReceiver = MyBroadcastReceiver()Java
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();Tạo một thực thể của
IntentFilter:Kotlin
val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")Java
IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");Chọn xem bộ nhận tín hiệu truyền tin có được xuất và hiển thị cho các ứng dụng khác trên thiết bị hay không. Nếu receiver này đang lắng nghe các thông báo truyền tin do hệ thống hoặc các ứng dụng khác gửi đi (kể cả các ứng dụng khác mà bạn sở hữu), hãy dùng cờ
RECEIVER_EXPORTED. Nếu thay vào đó, receiver này chỉ theo dõi các thông báo truyền tin do ứng dụng của bạn gửi, hãy dùng cờRECEIVER_NOT_EXPORTED.Kotlin
val listenToBroadcastsFromOtherApps = false val receiverFlags = if (listenToBroadcastsFromOtherApps) { ContextCompat.RECEIVER_EXPORTED } else { ContextCompat.RECEIVER_NOT_EXPORTED }Java
boolean listenToBroadcastsFromOtherApps = false; int receiverFlags = listenToBroadcastsFromOtherApps ? ContextCompat.RECEIVER_EXPORTED : ContextCompat.RECEIVER_NOT_EXPORTED;Đăng ký receiver bằng cách gọi
registerReceiver():Kotlin
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags)Java
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);Để ngừng nhận thông báo phát sóng, hãy gọi
unregisterReceiver(android.content.BroadcastReceiver). Hãy nhớ huỷ đăng ký bộ nhận khi bạn không cần đến nữa hoặc ngữ cảnh không còn hợp lệ.
Huỷ đăng ký bộ nhận tín hiệu truyền tin
Trong khi bộ nhận tín hiệu truyền tin được đăng ký, nó sẽ giữ một tham chiếu đến Ngữ cảnh mà bạn đã đăng ký. Điều này có thể gây ra rò rỉ nếu phạm vi đã đăng ký của receiver vượt quá phạm vi vòng đời Context. Ví dụ: điều này có thể xảy ra khi bạn đăng ký một bộ nhận trong phạm vi Hoạt động, nhưng bạn quên huỷ đăng ký bộ nhận đó khi hệ thống huỷ Hoạt động. Do đó, hãy luôn huỷ đăng ký bộ nhận tín hiệu truyền tin của bạn.
Kotlin
class MyActivity : ComponentActivity() {
private val myBroadcastReceiver = MyBroadcastReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags)
setContent { MyApp() }
}
override fun onDestroy() {
super.onDestroy()
// When you forget to unregister your receiver here, you're causing a leak!
this.unregisterReceiver(myBroadcastReceiver)
}
}
Java
class MyActivity extends ComponentActivity {
MyBroadcastReceiver myBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags);
// Set content
}
}
Đăng ký các receiver trong phạm vi nhỏ nhất
Bạn chỉ nên đăng ký bộ nhận tín hiệu truyền tin khi thực sự quan tâm đến kết quả. Chọn phạm vi nhỏ nhất có thể của receiver:
LifecycleResumeEffecthoặc các phương thức vòng đờionResume/onPausecủa hoạt động: Bộ nhận tín hiệu truyền tin chỉ nhận được thông tin cập nhật khi ứng dụng ở trạng thái đã tiếp tục.LifecycleStartEffecthoặc các phương thức vòng đờionStart/onStopcủa hoạt động: Bộ nhận tín hiệu truyền tin chỉ nhận được thông tin cập nhật khi ứng dụng ở trạng thái đã tiếp tục.DisposableEffect: Bộ nhận tín hiệu truyền tin chỉ nhận được thông tin cập nhật khi thành phần kết hợp nằm trong cây thành phần. Phạm vi này không được đính kèm vào phạm vi vòng đời của activity. Cân nhắc việc đăng ký receiver trên ngữ cảnh ứng dụng. Điều này là do về lý thuyết, composable có thể tồn tại lâu hơn phạm vi vòng đời của activity và làm rò rỉ activity.- Hoạt động
onCreate/onDestroy: Bộ nhận tín hiệu truyền tin nhận được thông tin cập nhật trong khi hoạt động ở trạng thái đã tạo. Đảm bảo huỷ đăng ký trongonDestroy()chứ không phảionSaveInstanceState(Bundle)vì phương thức này có thể không được gọi. - Một phạm vi tuỳ chỉnh: Ví dụ: bạn có thể đăng ký một receiver trong phạm vi
ViewModel, để receiver đó vẫn tồn tại sau khi hoạt động được tạo lại. Đảm bảo sử dụng ngữ cảnh ứng dụng để đăng ký bộ nhận, vì bộ nhận có thể tồn tại lâu hơn phạm vi vòng đời hoạt động và làm rò rỉ hoạt động.
Tạo thành phần kết hợp có và không có trạng thái
Compose có các thành phần kết hợp có trạng thái và không có trạng thái. Việc đăng ký hoặc huỷ đăng ký một bộ nhận tín hiệu truyền tin bên trong một composable sẽ khiến thành phần đó có trạng thái. Thành phần kết hợp không phải là một hàm xác định hiển thị cùng một nội dung khi được truyền cùng các tham số. Trạng thái nội bộ có thể thay đổi dựa trên các lệnh gọi đến broadcast receiver đã đăng ký.
Theo phương pháp hay nhất trong Compose, bạn nên chia các thành phần kết hợp thành phiên bản có trạng thái và không có trạng thái. Do đó, chúng tôi đề xuất bạn chuyển việc tạo bộ nhận tín hiệu truyền tin ra khỏi một composable để biến thành phần đó thành không trạng thái:
@Composable
fun MyStatefulScreen() {
val myBroadcastReceiver = remember { MyBroadcastReceiver() }
val context = LocalContext.current
LifecycleStartEffect(true) {
// ...
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, flags)
onStopOrDispose { context.unregisterReceiver(myBroadcastReceiver) }
}
MyStatelessScreen()
}
@Composable
fun MyStatelessScreen() {
// Implement your screen
}
Bộ nhận được khai báo trong tệp kê khai
Nếu bạn khai báo một bộ nhận tín hiệu truyền tin trong tệp kê khai, hệ thống sẽ chạy ứng dụng của bạn khi thông báo truyền tin được gửi đi. Nếu ứng dụng chưa chạy, hệ thống sẽ khởi chạy ứng dụng.
Để khai báo một bộ nhận tín hiệu truyền tin trong tệp kê khai, hãy thực hiện các bước sau:
Chỉ định phần tử
<receiver>trong tệp kê khai của ứng dụng.<!-- If this receiver listens for broadcasts sent from the system or from other apps, even other apps that you own, set android:exported to "true". --> <receiver android:name=".MyBroadcastReceiver" android:exported="false"> <intent-filter> <action android:name="com.example.snippets.ACTION_UPDATE_DATA" /> </intent-filter> </receiver>Bộ lọc ý định chỉ định các hành động truyền tin mà bộ nhận đăng ký.
Lớp con
BroadcastReceivervà triển khaionReceive(Context, Intent). Bộ nhận tín hiệu truyền tin trong ví dụ sau đây sẽ ghi nhật ký và hiển thị nội dung của thông báo truyền tin:Kotlin
class MyBroadcastReceiver : BroadcastReceiver() { @Inject lateinit var dataRepository: DataRepository override fun onReceive(context: Context, intent: Intent) { if (intent.action == "com.example.snippets.ACTION_UPDATE_DATA") { val data = intent.getStringExtra("com.example.snippets.DATA") ?: "No data" // Do something with the data, for example send it to a data repository: dataRepository.updateData(data) } } }Java
public static class MyBroadcastReceiver extends BroadcastReceiver { @Inject DataRepository dataRepository; @Override public void onReceive(Context context, Intent intent) { if (Objects.equals(intent.getAction(), "com.example.snippets.ACTION_UPDATE_DATA")) { String data = intent.getStringExtra("com.example.snippets.DATA"); // Do something with the data, for example send it to a data repository: if (data != null) { dataRepository.updateData(data); } } } }
Trình quản lý gói hệ thống sẽ đăng ký trình nhận khi ứng dụng được cài đặt. Sau đó, receiver sẽ trở thành một điểm truy cập riêng biệt vào ứng dụng của bạn, nghĩa là hệ thống có thể khởi động ứng dụng và gửi thông báo truyền tin nếu ứng dụng không chạy.
Hệ thống sẽ tạo một đối tượng thành phần BroadcastReceiver mới để xử lý từng thông báo truyền tin mà hệ thống nhận được. Đối tượng này chỉ hợp lệ trong khoảng thời gian diễn ra cuộc gọi đến onReceive(Context, Intent). Sau khi mã của bạn trả về từ phương thức này, hệ thống sẽ coi thành phần không còn hoạt động nữa.
Ảnh hưởng đến trạng thái xử lý
Việc BroadcastReceiver có đang hoạt động hay không sẽ ảnh hưởng đến quy trình có trong đó, điều này có thể làm thay đổi khả năng hệ thống bị buộc tắt. Một quy trình trên nền trước sẽ thực thi phương thức onReceive() của receiver. Hệ thống sẽ chạy quy trình này, trừ trường hợp chịu áp lực cực lớn về bộ nhớ.
Hệ thống sẽ huỷ kích hoạt BroadcastReceiver sau onReceive().
Mức độ quan trọng của quy trình lưu trữ của receiver phụ thuộc vào các thành phần ứng dụng của quy trình đó. Nếu quy trình đó chỉ lưu trữ một receiver được khai báo trong tệp kê khai, thì hệ thống có thể loại bỏ quy trình đó sau onReceive() để giải phóng tài nguyên cho các quy trình quan trọng hơn khác. Điều này thường xảy ra đối với những ứng dụng mà người dùng chưa từng tương tác hoặc chưa tương tác gần đây.
Do đó, broadcast receiver không được bắt đầu các luồng ở chế độ nền chạy trong thời gian dài.
Hệ thống có thể dừng quy trình bất cứ lúc nào sau onReceive() để thu hồi bộ nhớ, chấm dứt luồng đã tạo. Để duy trì quy trình, hãy lên lịch JobService từ bộ nhận bằng cách sử dụng JobScheduler để hệ thống biết quy trình vẫn đang hoạt động. Bài viết Tổng quan về hoạt động trong nền cung cấp thêm thông tin chi tiết.
Gửi thông báo
Android cung cấp 2 cách để ứng dụng gửi thông báo truyền tin:
- Phương thức
sendOrderedBroadcast(Intent, String)sẽ gửi thông báo truyền tin đến một receiver tại một thời điểm. Khi đến lượt trình thu nhận thực thi, nó có thể truyền kết quả đến trình thu nhận tiếp theo. Bạn cũng có thể huỷ hoàn toàn thông báo truyền tin để thông báo đó không đến được các trình thu nhận khác. Bạn có thể kiểm soát thứ tự chạy của các receiver trong cùng một quy trình ứng dụng. Để làm như vậy, hãy sử dụng thuộc tínhandroid:prioritycủa bộ lọc ý định phù hợp. Các receiver có cùng mức độ ưu tiên sẽ chạy theo một thứ tự tuỳ ý. - Phương thức
sendBroadcast(Intent)sẽ gửi thông báo truyền tin đến tất cả các receiver theo một thứ tự không xác định. Đây được gọi là Bản tin phát sóng thông thường. Cách này hiệu quả hơn, nhưng có nghĩa là các trình nhận không thể đọc kết quả từ các trình nhận khác, truyền dữ liệu nhận được từ thông báo truyền tin hoặc huỷ thông báo truyền tin.
Đoạn mã sau đây minh hoạ cách gửi thông báo truyền tin bằng cách tạo một Intent và gọi sendBroadcast(Intent).
Kotlin
val intent = Intent("com.example.snippets.ACTION_UPDATE_DATA").apply {
putExtra("com.example.snippets.DATA", newData)
setPackage("com.example.snippets")
}
context.sendBroadcast(intent)
Java
Intent intent = new Intent("com.example.snippets.ACTION_UPDATE_DATA");
intent.putExtra("com.example.snippets.DATA", newData);
intent.setPackage("com.example.snippets");
context.sendBroadcast(intent);
Thông báo phát đi được gói trong một đối tượng Intent. Chuỗi action của ý định phải cung cấp cú pháp tên gói Java của ứng dụng và xác định duy nhất sự kiện truyền tin. Bạn có thể đính kèm thêm thông tin vào ý định bằng putExtra(String, Bundle). Bạn cũng có thể giới hạn một thông báo truyền tin cho một nhóm ứng dụng trong cùng một tổ chức bằng cách gọi setPackage(String) trên ý định.
Hạn chế thông báo truyền phát bằng các quyền
Quyền cho phép bạn hạn chế truyền tin đến nhóm ứng dụng có một số quyền nhất định. Bạn có thể thực thi các quy định hạn chế đối với người gửi hoặc người nhận tin truyền đi.
Gửi thông báo có quyền
Khi gọi sendBroadcast(Intent, String) hoặc sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String,
Bundle), bạn có thể chỉ định một tham số quyền. Chỉ những receiver đã yêu cầu quyền đó bằng thẻ <uses-permission> trong tệp kê khai mới có thể nhận được thông báo truyền tin. Nếu quyền này là quyền nguy hiểm, bạn phải cấp quyền trước khi receiver có thể nhận được thông báo truyền tin. Ví dụ: mã sau đây gửi một thông báo truyền tin có quyền:
Kotlin
context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION)
Java
context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);
Để nhận thông báo truyền tin, ứng dụng nhận phải yêu cầu quyền như sau:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Bạn có thể chỉ định một quyền hệ thống hiện có như BLUETOOTH_CONNECT hoặc xác định một quyền tuỳ chỉnh bằng phần tử <permission>. Để biết thông tin về các quyền và tính bảo mật nói chung, hãy xem bài viết Quyền hệ thống.
Nhận thông báo phát sóng khi có quyền
Nếu bạn chỉ định một tham số quyền khi đăng ký một bộ nhận tín hiệu truyền tin (bằng registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) hoặc trong thẻ <receiver> trong tệp kê khai), thì chỉ những broadcaster đã yêu cầu quyền bằng thẻ <uses-permission> trong tệp kê khai mới có thể gửi Intent đến bộ nhận tín hiệu truyền tin. Nếu quyền này là quyền nguy hiểm, thì trình phát cũng phải được cấp quyền.
Ví dụ: giả sử ứng dụng nhận của bạn có một trình nhận được khai báo trong tệp kê khai như sau:
<!-- If this receiver listens for broadcasts sent from the system or from
other apps, even other apps that you own, set android:exported to "true". -->
<receiver
android:name=".MyBroadcastReceiverWithPermission"
android:permission="android.permission.ACCESS_COARSE_LOCATION"
android:exported="true">
<intent-filter>
<action android:name="com.example.snippets.ACTION_UPDATE_DATA" />
</intent-filter>
</receiver>
Hoặc ứng dụng nhận của bạn có một bộ nhận đã đăng ký theo bối cảnh như sau:
Kotlin
ContextCompat.registerReceiver(
context, myBroadcastReceiver, filter,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
null, // scheduler that defines thread, null means run on main thread
receiverFlags
)
Java
ContextCompat.registerReceiver(
context, myBroadcastReceiver, filter,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
null, // scheduler that defines thread, null means run on main thread
receiverFlags
);
Sau đó, để có thể gửi thông báo truyền tin đến những trình thu nhận đó, ứng dụng gửi phải yêu cầu quyền như sau:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Lưu ý về bảo mật
Sau đây là một số điểm cần cân nhắc về bảo mật khi gửi và nhận thông báo phát sóng:
Nếu nhiều ứng dụng đã đăng ký nhận cùng một thông báo truyền tin trong tệp kê khai, thì điều này có thể khiến hệ thống chạy nhiều ứng dụng, gây ảnh hưởng đáng kể đến cả hiệu suất của thiết bị và trải nghiệm người dùng. Để tránh điều này, hãy ưu tiên sử dụng đăng ký ngữ cảnh thay vì khai báo tệp kê khai. Đôi khi, chính hệ thống Android sẽ thực thi việc sử dụng các bộ nhận đã đăng ký theo bối cảnh. Ví dụ: thông báo truyền tin
CONNECTIVITY_ACTIONchỉ được gửi đến những trình thu nhận đã đăng ký theo bối cảnh.Đừng truyền thông tin nhạy cảm bằng ý định ngầm ẩn. Mọi ứng dụng đều có thể đọc thông tin này nếu đăng ký nhận thông báo truyền tin. Có 3 cách để kiểm soát đối tượng có thể nhận thông báo phát sóng của bạn:
- Bạn có thể chỉ định một quyền khi gửi một thông báo truyền tin.
- Trong Android 4.0 (cấp độ API 14) trở lên, bạn có thể chỉ định một gói bằng
setPackage(String)khi gửi một thông báo truyền phát. Hệ thống hạn chế thông báo truyền tin cho nhóm ứng dụng khớp với gói.
Khi bạn đăng ký một receiver, mọi ứng dụng đều có thể gửi các thông báo truyền tin có khả năng gây hại đến receiver của ứng dụng. Có một số cách để giới hạn các thông báo truyền tin mà ứng dụng của bạn nhận được:
- Bạn có thể chỉ định một quyền khi đăng ký bộ nhận tín hiệu truyền tin.
- Đối với các receiver được khai báo trong tệp kê khai, bạn có thể đặt thuộc tính android:exported thành "false" trong tệp kê khai. Receiver không nhận được thông báo truyền tin từ các nguồn bên ngoài ứng dụng.
Không gian tên cho các thao tác truyền tin là không gian tên chung. Đảm bảo rằng tên hành động và các chuỗi khác được viết trong một không gian tên mà bạn sở hữu. Nếu không, bạn có thể vô tình xung đột với các ứng dụng khác.
Vì phương thức
onReceive(Context, Intent)của receiver chạy trên luồng chính, nên phương thức này phải thực thi và trả về nhanh chóng. Nếu cần thực hiện công việc kéo dài, hãy cẩn thận khi tạo luồng hoặc bắt đầu các dịch vụ nền vì hệ thống có thể huỷ toàn bộ quy trình sau khionReceive()trả về. Để biết thêm thông tin, hãy xem phần Ảnh hưởng đến trạng thái quy trình. Để thực hiện công việc kéo dài, bạn nên:- Gọi
goAsync()trong phương thứconReceive()của receiver và truyềnBroadcastReceiver.PendingResultđến một luồng nền. Điều này giúp sự kiện phát sóng tiếp tục hoạt động sau khi người dùng quay lại từonReceive(). Tuy nhiên, ngay cả với phương pháp này, hệ thống vẫn mong bạn hoàn tất việc truyền tin rất nhanh (dưới 10 giây). Thao tác này cho phép bạn di chuyển thao tác sang một luồng khác để tránh làm gián đoạn luồng chính. - Lên lịch cho một công việc bằng
JobScheduler. Để biết thêm thông tin, hãy xem phần Lập lịch công việc thông minh.
- Gọi
Không bắt đầu các hoạt động từ broadcast receiver vì trải nghiệm người dùng sẽ bị gián đoạn; đặc biệt là nếu có nhiều receiver. Thay vào đó, hãy cân nhắc việc hiển thị một thông báo.