Bạn đã bao giờ nghe đến “đài phát thanh truyền hình” trong Android chưa? Nếu chưa thì mình bật mí nhé! Đó chính là Broadcast Receiver. Vậy broadcast receiver android là gì? Đúng như tên gọi của nó, nhiệm vụ chính của Broadcast receiver trong android là lan truyền thông tin trong hệ thống và chỉ những ứng dụng đăng kí mới nhận được.
Bài viết này mình sẽ chia sẻ tất cả những kiến thức quan trọng nhất về Broadcast Receiver Android.
Từ đó bạn có thể dễ dàng ứng dụng và sử dụng hiệu quả cho ứng dụng của mình
Nội dung chính của bài viết
1. Broadcast Receiver Android là gì?
Android Broadcast Receiver là một thành phần nơi bạn có thể đăng ký sự kiện của hệ thống hay ứng dụng. Bạn sẽ nhận được thông báo về các sự kiện đã đăng ký trước đó. Việc phát tin broadcast có thể bắt nguồn từ hệ thống hoặc từ các ứng dụng
Ví dụ: một số broadcast từ hệ thống như thông báo pin yếu, bật tắt màn hình, kết nối hay ngắt kết thiết bị ngoại vi…
Với cấp độ ứng dụng có thể kể ra như khi bạn download một file nhạc. Ứng dụng nhạc này sẽ nhận được thông báo về việc download, sau khi kết thúc sẽ đưa bài hát này vào danh sách để bật. Để thực hiện được việc này, ứng dụng nhạc cần thiết phải đăng ký cho sự kiện.
Không giống như Activity, Broadcast receiver trong android không hề có giao diện người dùng. Mặc dù nó có thể tạo thông báo trên thanh status bar.
Để truyền thông tin, Broadcast receiver sử dụng Intent để đóng gói dữ liệu. Nếu bạn đã đọc bài viết của mình về Intent trong Android thì sẽ dễ dàng hiểu về Broadcast receiver trong Android.
Bài viết này mình sẽ tập trung 3 vấn đề chính:
- Cách tạo Broadcast Receiver
- Hướng dẫn đăng kí để nhận thông báo từ Broadcast Receiver
- Cách gửi Broadcast Event/Intent
- Vấn đề bảo mật khi sử dụng Broadcast Receiver
2. Tạo một class Broadcast Receiver trong Android
Hãy theo dõi đoạn code bên dưới để xem cách tạo một broadcast receiver trong Android như nào:
class MyReceiver:BroadcastReceiver() { fun onReceive(context:Context, intent:Intent) { // This method is called when this BroadcastReceiver receives an Intent broadcast. Toast.makeText(context, "Action: " + intent.getAction(), Toast.LENGTH_SHORT).show() } }
Chúng ta sẽ tạo một class là MyReciever và được kế thừa từ BroadcastReceiver. Do BroadcastReceiver là một abstract class nên chúng ta bắt buộc phải override hàm onReceiver()
. Bất cứ khi nào có một sự kiện xảy ra, Android sẽ gọi hàm onReceiver()
.
Bạn để ý rằng, hàm onReceiver()
có 2 tham số được truyền vào là: Intent và Content. Với Intent, bạn sẽ nhận được những thông tin cần thiết từ hệ thống.
Còn Context sẽ giúp bạn có thể làm được một việc như start một Activity hay Service kiểu như context.startService(new Intent(this, TestService.class));
3. Đăng kí nhận Broadcast Receiver trong Android
Chúng ta đã hoàn thành việc tạo một Broadcast Receiver trong Android, nhưng để nó có thể nhận được thông báo từ hệ thống hay ứng dụng khác thì nó cần phải đăng kí. Có 2 cách đăng kí:
- Đăng kí bằng cách khai báo trong Manifest file
- Hoặc đăng kí bằng Kotlin code
#Cách 1: Đăng kí Broadcast Receiver trong Manifest File
Đây là cách có lẽ là dễ nhất
<receiver android:name="com.pycitup.pyc.MyReceiver" android:enabled="true" android:exported="true" > <intent-filter> <action android:name="com.pycitup.BroadcastReceiver" /> </intent-filter> </receiver>
Chúng ta sử dụng thẻ <receiver>
để đăng kí với một intent filter.
Nếu bạn quên chưa biết Intent filter là gì thì mời bạn đọc lại bài viết về intent này nhé: Intent trong Android: Vai trò và cách sử dụng
Về cơ bản có thể hiểu nôm na: Ứng dụng sử dụng intent filter để thông báo với hệ thống là ứng dụng của tôi đăng kí nhận thông báo.
Nhưng không phải nhận tất cả mà chỉ những thông báo nào phù hợp thì mới nhận. Ở đây thì chỉ những thông báo nào có action là: com.pycitup.BroadcastReceiver
thì mới nhận được
#Cách 2: Sử dụng code để đăng kí Broadcast Receiver
Chúng ta có thể thực hiện việc đăng kí bằng code Kotlin như sau:
var filter = IntentFilter("com.pycitup.BroadcastReceiver") var myReceiver = MyReceiver() fun registerReceiver(myReceiver, filter)
Tương tự như cách đăng kí bằng cách khai báo trong Manifest. Chúng ta cũng chỉ đăng kí nhận những thông báo có action là: com.pycitup.BroadcastReceiver.
Cơ bản action này cũng chỉ là một String mà bạn muốn đặt là gì cũng được. Thông thường thì người ta sẽ đặt tên action giống với tên package ứng dụng.
Có một sự khác biết lớn giữa 2 cách đăng kí là:Nếu đăng kí bằng cách sử dụng code thì khi activity bị stop thì việc lắng nghe broadcast cũng bị tạm dừng theo. Trong khi đăng kí qua manifest thì sẽ lắng nghe vĩnh viễn.
Do vậy, nếu bạn đăng kí receiver thông qua code thì nên hủy đăng kí khi activity bị stop. Kiểu như sau
protected fun onPause() { unregisterReceiver(mReceiver) super.onPause() }
Vậy cách đăng kí receiver nào là tốt nhất?
Lời khuyên của mình là tùy vào mục đích ứng dụng của bạn mà lựa chọn cách đăng kí cho phù hợp.
Nếu ứng dụng muốn cập nhập ngay trên màn hình điện thoại( như home screen, launcher, status bar, widget…) khi nhận thông báo từ hệ thống hay từ ứng dụng khác thì cách đăng kí qua manifest là thích hợp.
Trong khi cũng nhận những thông báo mà bạn chỉ muốn cập nhập khi người dùng đang ở ứng dụng thì cách đăng kí bằng code là hợp lý hơn. Vì bạn sẽ chủ động đăng kí/ hủy đăng kí để tối ưu hiệu năng hệ thống.
Ngoài ra, có một số trường hợp ngoại lệ như action Intent.ACTION_TIME_TICK
là bạn không thể đăng kí trong manifest mà phải đăng kí bằng code. Android làm như vậy là để tiết kiệm pin cho hệ thống.
- Đừng bỏ lỡ: Ngôn ngữ lập trình Kotlin – Tại sao phải học?
4. Gửi Broadcast Event/Intent
Chúng ta đã hoàn thành 2/3 công đoạn quan trọng của Broadcast Receiver trong Android. Phần cuối cùng chính là gửi thông báo(Broadcast).
val intent = Intent() intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) intent.setAction("com.pycitup.BroadcastReceiver") intent.putExtra("Foo", "Bar") sendBroadcast(intent)
Chúng ta tạo một intent, sau đó thiết lập action cho intent này. Ở đây là com.pycitup.BroadcastReceiver
. Chính là action mà chúng ta đã sử dụng để đăng kí ở phần phía trên bài viết. Dữ liệu sẽ được đưa vào intent thông qua hàm putExtra()
Cuối cùng gửi Broadcast bằng hàm sendBroadcast()
Mình giải thích thêm một chút nếu bạn nào để ý trong đoạn code trên có một flag: FLAG_INCLUDE_STOPPED_PACKAGES
. Cơ bản thì flag này cho phép ứng dụng vẫn nhận thông báo trong trường hợp ứng dụng được cài đặt nhưng chưa chạy lần nào hoặc bị tắt bởi công cụ quản lý hệ thống như task manager.
Các kiểu gửi Broadcasts
Nhìn chung, có 2 hình thức để gửi broadcasts:
- Gửi Broadcasts theo cách thông thường: Tức là chúng ta sử dụng hàm
Context.sendBradcast()
như bạn vừa thấy ở trên. Đây là cách gửi broadcast không đồng bộ.Và phía receiver cũng vậy. Các receiver được chạy theo một thứ tự không xác định, thường là cùng một lúc. Nhược điểm của cách gửi này là các reciever không thể sử dụng kết quả của nhau - Gửi Broadcasts theo thứ tự: Để làm được điều này, chúng ta sử dụng hàm
Context.sendOrderedBroadcast()
và nó chỉ định rõ receiver được nhận tại một thời điểm. Thứ tự Receiver nhận kiểm soát bởi thuộc tínhandroid:priority
. Reciever có cùng mức độ ưu tiên sẽ được thực hiện theo thứ tự ngẫu nhiên. Khi mỗi reciever thực hiện, nó có thể chuyển kết quả đến receiver kế tiếp hoặc hủy bỏ toàn bộ chuỗi broadcasts để không cho reciever khác nhận được
Dưới đây là ví dụ cho cách gửi Broadcast theo thứ tự
// MySecondReceiver.kt class MySecondReceiver:BroadcastReceiver() { private val TAG = MySecondReceiver::class.java!!.getSimpleName() fun onReceive(context:Context, intent:Intent) { val results = getResultExtras(true) results.putString("hierarchy", TAG) Log.d(TAG, "MySecondReceiver") } }
// MyReceiver.kt class MyReceiver:BroadcastReceiver() { private val TAG = MyReceiver::class.java!!.getSimpleName() fun onReceive(context:Context, intent:Intent) { val results = getResultExtras(true) val hierarchy = results.getString("hierarchy") results.putString("hierarchy", hierarchy + "->" + TAG) Log.d(TAG, "MyReceiver") } }
Cuối cùng là khai báo đăng kí trong Manifest
<receiver android:name="com.pycitup.pyc.MyReceiver" > <intent-filter android:priority="1"> <action android:name="com.pycitup.BroadcastReceiver" /> </intent-filter> </receiver> <receiver android:name="com.pycitup.pyc.MySecondReceiver" > <intent-filter android:priority="2"> <action android:name="com.pycitup.BroadcastReceiver" /> </intent-filter> </receiver>
Các bạn để ý đến mức độ ưu tiên của receiver thông qua thẻ android:priority. Mức ưu tiên càng cao thì sẽ được nhận trước tiên. Mặc định, mức độ ưu tiên là 0
Tiếp theo là phần gửi Broadcast trong MainActivity.onCreate ()
:
val filter = IntentFilter("com.pycitup.BroadcastReceiver") registerReceiver(object:BroadcastReceiver() { fun onReceive(context:Context, intent:Intent) { val results = getResultExtras(true) val hierarchy = results.getString("hierarchy") results.putString("hierarchy", hierarchy + "->" + TAG) Log.d(TAG, "Anonymous class broadcast receiver") } }, filter) val intent = Intent("com.pycitup.BroadcastReceiver") sendOrderedBroadcast(intent, null, object:BroadcastReceiver() { fun onReceive(context:Context, intent:Intent) { val results = getResultExtras(true) val hierarchy = results.getString("hierarchy") println(hierarchy) Log.d(TAG, "Final Receiver") } }, null, Activity.RESULT_OK, null, null)
Và đây là kết quả khi bạn chạy ứng dụng
﹕ MySecondReceiver ﹕ MyReceiver ﹕ Anonymous class broadcast receiver ﹕ MySecondReceiver->MyReceiver->MainActivity ﹕ Final Receiver
Như mình nói ở trên, bất kì receiver nào cũng có thể hủy toàn bộ chuỗi Broadcast để không cho các receiver khác nhận được. Đơn giản là gọi hàm abortBroadcast()
- Dành cho bạn: Tích hợp Google Drive vào ứng dụng Android từ A-Z
5. Local BroadcastManager – Gửi Broadcast trong nội bộ ứng dụng
Như các bạn đã biết thì khi gửi Broadcast thì bất kì ứng dụng nào đăng kí đều nhận được. Đôi khi vì lý do bảo mật cho ứng dụng mà chúng ta không muốn bất kì ứng dụng nào khác nhận được. Những thông tin này chỉ được gửi bên trong ứng dụng.
LocalBroadcast sẽ giúp bạn làm được điều đó.
LocalBroadcastManager.getInstance(this).registerReceiver(object:BroadcastReceiver() { fun onReceive(context:Context, intent:Intent) { val message = intent.getStringExtra("foo") Log.d("LocalBroadcastManager", "foo : " + message) } }, IntentFilter("my-custom-event"))
Còn đây là cách gửi local broadcast
val intent = Intent("my-custom-event") intent.putExtra("foo", "bar") LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
Và đừng quên hủy đăng kí trong hàm onPause()
của Activity
protected fun onPause() { LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver) super.onPause() }
6. Permissions
Có một số Broadcast cần phải yêu cầu permission. Tất nhiên là những permission này phải được sự chấp thuận của người dùng.
Ví dụ: Intent.ACTION_BOOT_COMPLETED
yêu cầu permission RECEIVE_BOOT_COMPLETED
Các bạn khai báo permission trong Manifest như sau
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Tất nhiên, đây chỉ là khai báo thôi, từ Android M trở lên, bạn cần phải viết thêm code để yêu cầu người dùng chấp thuận permission này cho ứng dụng
Mặc dù hầu hết Broadcast không yêu cầu permission, tuy nhiên cẩn tắc vô áy náy, bạn có thể kiểm tra tại đây
7. Tổng kết
Như vậy là chúng ta đã đi qua tất cả các vấn đề của Broadcast Receiver trong Android. Mặc dù BroadcastReceiver không phải là vấn đề khó nhưng cũng có nhiều điểm thú vị phải không?
Bài viết sau chúng ta sẽ cùng nhau tìm hiểu về xử lý đa luồng thông qua thread, Asynctask trong Android
Các bạn đừng quên để lại comment nếu có bất kì thắc mắc nào nhé
Mình nghĩ trang web kiến thức nên có thêm dòng thông tin bài đăng được viết trong thời gian nào, để người đọc dễ theo dõi , vì kiến thức thay đổi khá nhanh, cảm ơn bạn vì bài viết.
Cám ơn bạn đã góp ý. Bên mình sẽ ghi nhận và cố gắng cải thiện để nội dung thiết thực hơn ạ.
Nhờ a hướng dẫn dùm e, e muốn viết 1 app widget, nhiệm vụ của nó chỉ là: kết nối bluetooth vào 1 địa chỉ đã lưu, nếu kết nối được, thì hiện chuổi nhận được từ bluetooth vào textview sẽ được gửi 1s/1 lần. Nếu không kết nối được thì thử kết nối lại mỗi 5ph.
E cảm ơn a rất nhiều
Mình nghĩ widget nó có giới hạn từ OS vì vấn đề pin, nên bạn sẽ khó để widget chạy ngầm và tự động connect tới bluetooth. Thay vào đó, bạn nên sử dụng một service chạy ngầm, còn widget chỉ nên hiển thị kết quả
E đã làm 1 service connect bluetooth và nhận bluetooth gatt callback rồi, đã có thể hiển thị trên activity rồi. Nhưng e không biết làm sao có thể show giá trị lên widget . Xin a chỉ giúp dùm e
Cách đơn giản là mỗi khi muốn cập nhật hiển thị trên widget, từ service bạn bắn broadcast sử dụng intent và phía widget sẽ nhận dữ liệu ấy qua intent
Bạn tham khảo code snippet này nhé: https://stackoverflow.com/a/9466362
À e hiểu rồi, mình sẽ bắn broadcast ở gattcallback character change, rồi ở widget mình chỉ cần receive rồi hiện lên text thôi, đúng ko
Cảm ơn a nhiều
Đúng rùi bạn
A cho e hỏi , e đã tạo service kết nối bluetooth rồi, trong gattcallback e đã dùng intent để bắn broadcast qua widget rồi, widget đã thay đổi theo ý muốn, nhưng vì service của e được binder trong activity. Nên mỗi khi e thoát ra trang chủ thì service nó unbind luôn. Làm widget đứng luôn. Vậy làm sao để dù khi thoát khỏi app, thì widget vẫn cập nhật cho mình. Nếu được a có thể cho e xin zalo hoặc messenger để mình tiện trao đổi không?
Xin cảm ơn
bạn tham khảo khái niệm foreground Service trong android nhé
A có nhận viết app theo yêu cầu không?
Thank
Mình không bạn à.
Có 2 lớp broadcast receiver chính là gì vậy ạ?
Mình chưa hiểu câu hỏi của bạn? Bạn có thể nói rõ hơn được không?