Các ứng dụng Android có thể gửi hoặc nhận thông báo phát đi từ hệ thống Android và các ứng dụng Android khác, tương tự như xuất bản-đăng ký mẫu thiết kế. Những thông báo này được gửi khi một sự kiện quan tâm xảy ra. Ví dụ: hệ thống Android gửi thông báo khi các sự kiện hệ thống khác nhau xảy ra, chẳng hạn như khi hệ thống khởi động hoặc thiết bị bắt đầu sạc. Chiến dịch Quảng cáo ứng dụng cũng có thể 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ề điều gì đó mà họ có thể quan tâm (ví dụ: một số dữ liệu mới đã tải xuống).
Hệ thống tối ưu hoá việc phân phối tin truyền để duy trì tình trạng hệ thống tối ưu. Do đó, thời gian phân phối của thông báo không được được đảm bảo. Các ứng dụng cần giao tiếp liên quy trình có độ trễ thấp nên hãy cân nhắc đến các dịch vụ ràng buộc.
Các ứng dụng có thể đăng ký nhận các thông báo truyền tin cụ thể. Khi thông báo được gửi đi, hệ thống tự động định tuyến thông báo truyền phát đến các ứng dụng đã đăng ký nhận loại tin truyền phát cụ thể đó.
Nói chung, thông báo có thể được dùng làm hệ thống nhắn tin trên các ứng dụng và nằm 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 công việc trong nền có thể khiến hệ thống hoạt động chậm.
Giới thiệu về thông báo của hệ thống
Hệ thống tự động gửi thông báo khi các sự kiện hệ thống khác nhau diễn ra, chẳng hạn như khi hệ thống chuyển vào và thoát khỏi chế độ trên máy bay. Hệ thống thông báo truyền tin sẽ được gửi đến tất cả ứng dụng đã đăng ký nhận sự kiện.
Thông báo phát đi được gói trong một Intent
đối tượng có chuỗi hành động xác định sự kiện đã xảy ra (ví dụ:
android.intent.action.AIRPLANE_MODE
). Ý định cũng có thể bao gồm
thông tin bổ sung được nhóm vào trường bổ sung. Ví dụ: máy bay
ý định của chế độ bao gồm một thuộc tính boolean bổ sung cho biết liệu Máy bay có phải là Máy bay hay không
Chế độ đang bật.
Để biết thêm thông tin về cách đọc ý định và lấy chuỗi hành động từ về một ý định, hãy xem phần Ý định và ý định Bộ lọc.
Để biết danh sách đầy đủ các thao tác truyền tin của hệ thống, hãy xem
BROADCAST_ACTIONS.TXT
trong SDK Android. Mỗi hành động truyền tin có một
trường không đổi liên kết với trường đó. 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 hành động truyền tin
có sẵn trong trường hằng số liên kết.
Thay đổi đối với thông báo của 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 hệ thống truyền tin hành xử. Hãy lưu ý những thay đổi sau để hỗ trợ tất cả phiên bản Android.
Android 14
Khi các ứng dụng đang ở trong bộ nhớ đệm
trạng thái, phân phối tin truyền phát là
được tối ưu hoá để cải thiện tình trạng hệ thống. Ví dụ: các thông báo truyền tin ít quan trọng hơn là
với tên ACTION_SCREEN_ON
bị trì hoãn khi ứng dụng đang ở trạng thái đã lưu vào bộ nhớ đệm. Sau khi ứng dụng chuyển từ bộ nhớ đệm
trạng thái thành quá trình hoạt động
vòng đời, hệ thống sẽ phân phối
bất kỳ thông báo truyền phát bị trì hoãn nào.
Thông báo truyền tin quan trọng được khai báo trong tệp kê khai tạm thời xóa các ứng dụng khỏi bộ nhớ đệm để phân phối.
Android 9
Kể từ Android 9 (API cấp 28),
NETWORK_STATE_CHANGED_ACTION
thông báo truyền tin không nhận được thông tin về vị trí hoặc thông tin cá nhân của người dùng
dữ liệu nhận dạng cá nhân.
Ngoài ra, nếu ứng dụng được cài đặt trên một thiết bị chạy Android 9 trở lên,
thông báo của hệ thống từ Wi-Fi không chứa SSID, BSSID, kết nối
hoặc quét kết quả. Để nhận thông tin này, hãy gọi
getConnectionInfo()
thay thế.
Android 8.0
Kể từ Android 8.0 (API cấp 26), hệ thống áp dụng thêm các chế độ cài đặt đối với các dịch vụ nhận đã 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ể sử dụng tệp kê khai để khai báo trình nhận cho hầu hết các thông báo truyền tin ngầm ẩn (thông báo truyền tin không nhắm mục tiêu) ứng dụng của bạn). Bạn vẫn có thể sử dụng bộ nhận đă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 (API cấp 24) trở lên không gửi hệ thống sau thông báo truyền tin:
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 truyền tin CONNECTIVITY_ACTION
đang sử dụng registerReceiver(BroadcastReceiver, IntentFilter)
.
Việc khai báo trình nhận trong tệp kê khai không hoạt động.
Đang nhận thông báo truyền tin
Ứng dụng có thể nhận thông báo truyền tin theo hai cách: thông qua dịch vụ nhận đã khai báo trong tệp kê khai và bộ thu đã đăng ký theo bối cảnh.
Trình nhận được khai báo trong tệp kê khai
Nếu bạn khai báo broadcast receiver trong tệp kê khai của mình, hệ thống sẽ khởi chạy ứng dụng (nếu ứng dụng chưa chạy) khi thông báo truyền tin được gửi.
Để khai báo broadcast receiver trong tệp kê khai, hãy thực hiện các bước sau:
Chỉ định
<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="APP_SPECIFIC_BROADCAST" /> </intent-filter> </receiver>
Bộ lọc ý định chỉ định các hành động truyền tin mà bộ thu của bạn đăng ký.
Lớp con
BroadcastReceiver
và triển khaionReceive(Context, Intent)
. Chiến lược phát hành đĩa đơn broadcast receiver trong nhật ký mẫu sau đây và hiển thị nội dung của chương trình phát sóng:Kotlin
private const val TAG = "MyBroadcastReceiver" class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { StringBuilder().apply { append("Action: ${intent.action}\n") append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n") toString().also { log -> Log.d(TAG, log) val binding = ActivityNameBinding.inflate(layoutInflater) val view = binding.root setContentView(view) Snackbar.make(view, log, Snackbar.LENGTH_LONG).show() } } } }
Java
public class MyBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "MyBroadcastReceiver"; @Override public void onReceive(Context context, Intent intent) { StringBuilder sb = new StringBuilder(); sb.append("Action: " + intent.getAction() + "\n"); sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n"); String log = sb.toString(); Log.d(TAG, log); ActivityNameBinding binding = ActivityNameBinding.inflate(layoutInflater); val view = binding.root; setContentView(view); Snackbar.make(view, log, Snackbar.LENGTH_LONG).show(); } }
Để bật tính năng liên kết thành phần hiển thị, định cấu hình viewBinding ở cấp mô-đun tệp build.gradle.
Trình quản lý gói hệ thống đă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 vào ứng dụng của bạn, tức là hệ thống có thể khởi động ứng dụng và phân phối thông báo truyền tin nếu ứng dụng không hiện đang chạy.
Hệ thống tạo một thành phần BroadcastReceiver
mới
để xử lý từng thông báo truyền tin mà đối tượng đó nhận được. Đối tượng này chỉ hợp lệ
trong thời lượng của lệnh gọi đến onReceive(Context, Intent)
. Sau khi mã
trả về từ phương thức này, hệ thống sẽ coi thành phần không còn
đang hoạt động.
Bộ nhận đã đăng ký theo bối cảnh
Bộ thu đã đăng ký theo bối cảnh sẽ nhận thông báo, miễn là
ngữ cảnh là hợp lệ. Ví dụ: nếu bạn đăng ký trong một
Activity
ngữ cảnh, bạn sẽ nhận được thông báo miễn là hoạt động không bị huỷ bỏ. Nếu bạn
đăng ký với 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
đang chạy.
Để đăng ký trình nhận 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.13.1" // 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.0.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-alpha02" }
Kotlin
dependencies { val core_version = "1.13.1" // 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.0.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-alpha02") }
Tạo một thực thể của
BroadcastReceiver
:Kotlin
val br: BroadcastReceiver = MyBroadcastReceiver()
Java
BroadcastReceiver br = new MyBroadcastReceiver();
Tạo một thực thể của
IntentFilter
:Kotlin
val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
Java
IntentFilter filter = new IntentFilter(APP_SPECIFIC_BROADCAST);
Chọn xem có xuất và hiển thị broadcast receiver với các ứng dụng khác trên thiết bị. Nếu receiver này đang nghe các thông báo truyền tin đã gửi từ hệ thống hoặc từ các ứng dụng khác (ngay cả những ứng dụng khác mà bạn sở hữu) hãy sử dụng Cờ
RECEIVER_EXPORTED
. Nếu thay vào đó, bộ thu này chỉ nghe thông báo truyền tin do ứng dụng của bạn gửi, hãy sử 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; if (listenToBroadcastsFromOtherApps) { receiverFlags = ContextCompat.RECEIVER_EXPORTED; } else { receiverFlags = ContextCompat.RECEIVER_NOT_EXPORTED; }
Đăng ký receiver bằng cách gọi
registerReceiver()
:Kotlin
ContextCompat.registerReceiver(context, br, filter, receiverFlags)
Java
ContextCompat.registerReceiver(context, br, filter, receiverFlags);
Để ngừng nhận thông báo truyền tin, hãy gọi
unregisterReceiver(android.content.BroadcastReceiver)
. Hãy nhớ huỷ đăng ký trình nhận khi bạn không còn cần đến hoặc ngữ cảnh không còn hợp lệ.Hãy chú ý đến nơi bạn đăng ký và huỷ đăng ký người nhận, vì ví dụ: nếu đăng ký trình nhận trong
onCreate(Bundle)
bằng cách sử dụng ngữ cảnh của hoạt động, bạn nên huỷ đăng ký trongonDestroy()
để ngăn rò rỉ trình thu nhận ra khỏi ngữ cảnh hoạt động. Nếu bạn đăng ký là một receiver trongonResume()
, bạn nên huỷ đăng ký trongonPause()
để ngăn đăng ký nó nhiều lần (Nếu bạn không muốn nhận thông báo khi bị tạm dừng và điều này có thể làm giảm chi phí hệ thống không cần thiết). Không nên huỷ đăng ký trongonSaveInstanceState(Bundle)
, vì lệnh này không được gọi nếu người dùng quay lại ngăn xếp nhật ký.
Ảnh hưởng đến trạng thái quy trình
Cho dù BroadcastReceiver
của bạn
đang hoạt động hoặc không ảnh hưởng đến quá trình chứa trong đó, điều này có thể làm thay đổi
khả năng hạ gục hệ thống. Quy trình trên nền trước sẽ thực thi phương thức onReceive()
của trình nhận. Chiến lược phát hành đĩa đơn
hệ thống sẽ chạy quy trình này
ngoại trừ áp lực bộ nhớ cực kỳ nghiêm trọng.
BroadcastReceiver bị huỷ kích hoạt sau onReceive()
. Máy chủ lưu trữ của người nhận
chỉ có ý nghĩa quan trọng như các thành phần ứng dụng. Nếu quá trình đó chỉ lưu trữ
trình thu nhận được khai báo trong tệp kê khai (điều này thường xảy ra đối với các ứng dụng mà người dùng chưa bao giờ
hoặc chưa tương tác gần đây), hệ thống có thể tắt sau onReceive()
để khiến
sẵn có cho các quá trình quan trọng hơn khác.
Do đó, broadcast receiver không nên bắt đầu các luồng chạy trong nền trong thời gian dài.
Hệ thống có thể dừng quá trình này bất cứ lúc nào sau ngày onReceive()
để xác nhận lại quyền sở hữu
bộ nhớ, chấm dứt luồng đã tạo. Để duy trì quy trình, hãy lên lịch
JobService
từ trình nhận bằng cách sử dụng JobScheduler
để hệ thống biết là quá trình vẫn đang hoạt động.
Bài viết Tổng quan về công việc ở chế độ nền cung cấp thêm thông tin chi tiết.
Đang gửi thông báo truyền phát
Android cung cấp 3 cách để ứng dụng gửi thông báo truyền tin:
sendOrderedBroadcast(Intent, String)
sẽ gửi thông báo đến một trình thu nhận tại một thời điểm. Khi mỗi receiver thực thi nó có thể truyền kết quả đến trình thu nhận tiếp theo hoặc có thể huỷ hoàn toàn truyền phát để không truyền tới đầu thu. Trình nhận đơn đặt hàng chạy vào có thể được kiểm soát bằng android:Priority của bộ lọc ý định phù hợp; các receiver có cùng mức độ ưu tiên sẽ được chạy theo thứ tự tuỳ ý.- Phương thức
sendBroadcast(Intent)
sẽ gửi truyền tin đến tất cả các receiver theo thứ tự không xác định. Đây gọi là Thông thường Truyền phát. Phương thức này hiệu quả hơn nhưng có nghĩa là các receiver không thể đọc kết quả từ các receiver khác, truyền dữ liệu nhận được từ thông báo truyền tin, hoặc huỷ truyền phát.
Đ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
Ý định và gọi sendBroadcast(Intent)
.
Kotlin
Intent().also { intent -> intent.setAction("com.example.broadcast.MY_NOTIFICATION") intent.putExtra("data", "Nothing to see here, move along.") sendBroadcast(intent) }
Java
Intent intent = new Intent(); intent.setAction("com.example.broadcast.MY_NOTIFICATION"); intent.putExtra("data", "Nothing to see here, move along."); sendBroadcast(intent);
Thông báo truyền tin được gói trong một đối tượng Intent
.
Chuỗi hành động 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 phát sóng. Bạn có thể đính kèm thông tin bổ sung
cho ý định bằng putExtra(String, Bundle)
.
Bạn cũng có thể giới hạn việc truyền tin đến 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 tin bằng quyền
Quyền cho phép bạn hạn chế thông báo truyền tin cho nhóm ứng dụng chứa một số quyền nhất định. Bạn có thể thực thi các hạn chế đối với người gửi hoặc trình nhận tín hiệu truyền tin.
Gửi bằng quyền
Khi bạn 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 người nhận đã yêu cầu quyền đó bằng
Kotlin
sendBroadcast(Intent(BluetoothDevice.ACTION_FOUND), Manifest.permission.BLUETOOTH_CONNECT)
Java
sendBroadcast(new Intent(BluetoothDevice.ACTION_FOUND), Manifest.permission.BLUETOOTH_CONNECT)
Để nhận thông báo truyền tin, ứng dụng nhận phải yêu cầu quyền như được hiển thị bên dưới:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
Bạn có thể chỉ định quyền hệ thống hiện có như
BLUETOOTH_CONNECT
hoặc định nghĩa một quyền tuỳ chỉnh bằng thuộc tính
Phần tử <permission>
. Cho
về quyền và bảo mật nói chung, hãy xem phần Hệ thống
Quyền.
Nhận bằng quyền
Nếu bạn chỉ định một tham số quyền khi đăng ký broadcast receiver
(bằng registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
hoặc trong
Thẻ <receiver>
trong
), thì chỉ những đài phát đã yêu cầu cấp quyền bằng
Thẻ <uses-permission>
trong tệp kê khai (và sau đó được cấp quyền nếu
nguy hiểm) có thể gửi Ý định cho dịch vụ nhận.
Ví dụ: giả sử ứng dụng nhận dữ liệu của bạn có một receiver đã khai báo trong tệp kê khai là được hiển thị bên dưới:
<receiver android:name=".MyBroadcastReceiver"
android:permission="android.permission.BLUETOOTH_CONNECT">
<intent-filter>
<action android:name="android.intent.action.ACTION_FOUND"/>
</intent-filter>
</receiver>
Hoặc ứng dụng nhận dữ liệu của bạn có bộ nhận đã đăng ký theo bối cảnh như dưới đây:
Kotlin
var filter = IntentFilter(Intent.ACTION_FOUND) registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null )
Java
IntentFilter filter = new IntentFilter(Intent.ACTION_FOUND); registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null );
Sau đó, để có thể gửi thông báo truyền tin đến các receiver đó, ứng dụng gửi phải hãy yêu cầu quyền như trình bày dưới đây:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
Các phương pháp hay nhất và những điều cần cân nhắc về bảo mật
Dưới đây là một số điểm cần cân nhắc về bảo mật và các phương pháp hay nhất khi gửi thư đang nhận thông báo truyền phát:
Nếu nhiều ứng dụng đã đăng ký nhận cùng một nội dung truyền phát trong tệp kê khai, nó có thể khiến hệ thống khởi chạy nhiều ứng dụng, gây ra ảnh hưởng đáng kể đến cả hiệu suất thiết bị và trải nghiệm người dùng. Cần tránh ưu tiên sử dụng quy trình đăng ký theo bối cảnh hơn là khai báo tệp kê khai. Đôi khi, chính hệ thống Android thực thi việc sử dụng dữ liệu đã đăng ký theo bối cảnh đầu thu. Ví dụ: thông báo truyền tin
CONNECTIVITY_ACTION
được phân phối chỉ cho bộ thu đã đăng ký theo bối cảnh.Không được truyền thông tin nhạy cảm bằng ý định ngầm ẩn. Chiến lược phát hành đĩa đơn bất kỳ ứng dụng nào đã đăng ký nhận thông báo truyền tin đều có thể đọc thông tin này. Có ba cách để kiểm soát những ai có thể nhận thông báo của bạn:
- Bạn có thể chỉ định một quyền khi gửi thông báo truyền tin.
- Trong Android 4.0 trở lên, bạn có thể chỉ định
package bằng
setPackage(String)
khi gửi một truyền tin. Hệ thống chỉ cho phép phát đi thông báo ở một nhóm ứng dụng khớp với gói.
Khi bạn đăng ký bộ nhận, bất kỳ ứng dụng nào cũng có thể gửi phần mềm độc hại tiềm ẩn truyền tin đến bộ thu của ứng dụng. Có một số cách để hạn chế 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ý broadcast receiver.
- Đối với các trình thu nhận được khai báo trong tệp kê khai, bạn có thể đặt android:exported thuộc tính "false" trong tệp kê khai. Người nhận không nhận được thông báo truyền phát từ các nguồn bên ngoài ứng dụng.
Không gian tên dành cho các thao tác truyền tin là không gian chung. Hãy đảm bảo rằng tên hành động và các chuỗi khác được viết trong 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.
Do phương thức
onReceive(Context, Intent)
của trình nhận chạy trên luồng chính, nó sẽ thực thi và trả về nhanh chóng. Nếu bạn cần thực hiện tác vụ lâu dài, cẩn thận về việc tạo luồng hoặc bắt đầu các dịch vụ nền vì hệ thống có thể dừng toàn bộ quá trình sau Trả lại hàng với mức phíonReceive()
. Để biết thêm thông tin, hãy xem phần Ảnh hưởng đến quy trình trạng thái Để thực hiện công việc lâu dài, chúng ta đề xuất:- Đang gọi
goAsync()
trong phương thứconReceive()
của receiver và truyềnBroadcastReceiver.PendingResult
đến luồng nền. Thao tác này sẽ duy trì hoạt động của nội dung truyền phát sau khi trở lại từonReceive()
. Tuy nhiên, ngay cả với phương pháp này, hệ thống vẫn mong muốn bạn kết thúc bằng truyền tin rất nhanh (dưới 10 giây). Ứng dụng này cho phép bạn di chuyển thao tác với một luồng khác để tránh sự cố luồng chính. - Lên lịch công việc bằng
JobScheduler
. Để biết thêm thông tin, hãy xem phần Công việc thông minh Lên lịch.
- Đang gọi
Không bắt đầu hoạt động từ broadcast receiver vì trải nghiệm người dùng gây khó chịu; đặc biệt nếu có nhiều hơn một receiver. Thay vào đó, hãy cân nhắc đang hiện một thông báo.