Tổng quan về truyền tin

Ứ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ế phát hành-đăng ký. Hệ thống và ứ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ư khởi động hệ thống hoặc sạc thiết bị. Ứ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 có thể khiến chúng quan tâm (ví dụ: tải dữ liệu mới xuống).

Ứng dụng có thể đăng ký nhận thông báo truyền tin 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 các ứng dụng đã đăng ký nhận loại thông báo truyền tin cụ thể đó.

Nhìn 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 thông báo truyền tin và chạy các công việc ở chế độ nền có thể 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 nhiều sự kiện hệ thống xảy ra, chẳng hạn như khi hệ thống chuyển vào và ra khỏi Chế độ trên máy bay. Tất cả ứng dụng đã đăng ký sẽ nhận được các thông báo truyền tin này.

Đối tượng Intent gói thông báo truyền tin. 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 đóng gói vào trường bổ sung. Ví dụ: ý định Chế độ trên máy bay bao gồm một boolean bổ sung cho biết Chế độ trên máy bay có đang bật hay không.

Để biết thêm thông tin về cách đọc ý định và nhận chuỗi hành động từ một ý định, hãy xem phần Ý định và bộ lọc ý định.

Thao tác truyền tin của hệ thống

Để xem danh sách đầy đủ các hành động truyền tin hệ thống, hãy xem tệp BROADCAST_ACTIONS.TXT trong SDK Android. Mỗi hành động truyền tin đều có một trường hằng số liên kết với hành động đó. Ví dụ: giá trị của hằng số ACTION_AIRPLANE_MODE_CHANGEDandroid.intent.action.AIRPLANE_MODE. Tài liệu về từng hành động truyền tin có trong trường hằng số liên kết.

Thay đổi đối với thông báo truyền tin 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 thông báo truyền tin hệ thống. Hãy lưu ý những thay đổi sau đây để hỗ trợ tất cả phiên bản Android.

Android 14

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 thông báo truyền tin để đảm bảo tình trạng hệ thống. Ví dụ: hệ thống trì hoãn các thông báo truyền tin hệ thống ít quan trọng hơn như ACTION_SCREEN_ON trong khi ứng dụng ở trạng thái đã lưu vào bộ nhớ đệm. Sau 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 thông báo truyền tin bị trì hoãn.

Các thông báo quan trọng được khai báo trong tệp kê khai sẽ tạm thời xoá ứng dụng khỏi trạng thái đã lưu vào bộ nhớ đệm để phân phối.

Android 9

Kể từ Android 9 (API cấp 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 (API cấp 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 thông báo truyền Wi-Fi. Để nhận thông tin này, hãy gọi getConnectionInfo().

Android 8.0

Kể từ Android 8.0 (API cấp 26), hệ thống sẽ áp dụng thêm các quy định hạn chế đối với trình nhận đượ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ể 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 đến ứng dụng của bạn một cách cụ thể). Bạn vẫn có thể sử dụng trình thu đã đăng ký theo ngữ 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 các thông báo truyền tin hệ thống 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 truyền tin CONNECTIVITY_ACTION bằng registerReceiver(BroadcastReceiver, IntentFilter). Không khai báo được trình nhận trong tệp kê khai.

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 trình thu phát sóng được đăng ký theo ngữ cảnh và trình thu phát sóng được khai báo trong tệp kê khai.

Bộ thu đã đăng ký theo bối cảnh

Bộ thu được đă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 hợp lệ. Quá trình này thường diễn ra giữa các lệnh gọi đến registerReceiverunregisterReceiver. 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 hoạt động. 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 trình nhận có ngữ cảnh, hãy thực hiện các bước sau:

  1. 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.15.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.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.15.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.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")
    }
    
  2. Tạo một thực thể của BroadcastReceiver:

    Kotlin

    val myBroadcastReceiver = MyBroadcastReceiver()
    

    Java

    MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
    
  3. 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");
    
  4. Chọn xem có xuất và hiển thị broadcast receiver cho các ứng dụng khác trên thiết bị hay không. Nếu broadcast receiver này đang nghe các thông báo truyền tin từ hệ thống hoặc từ các ứng dụng khác (ngay cả các ứng dụng khác mà bạn sở hữu), hãy sử dụng cờ RECEIVER_EXPORTED. Nếu trình thu này chỉ nghe các thông báo 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;
    int receiverFlags = listenToBroadcastsFromOtherApps
            ? ContextCompat.RECEIVER_EXPORTED
            : ContextCompat.RECEIVER_NOT_EXPORTED;
    
  5. Đăng ký trình thu nhận bằng cách gọi registerReceiver():

    Kotlin

    ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags)
    

    Java

    ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);
    
  6. Để 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 thu khi bạn không cần nữa hoặc ngữ cảnh không còn hợp lệ.

Huỷ đăng ký broadcast receiver

Trong khi được đăng ký, broadcast receiver sẽ giữ một tệp tham chiếu đến Context 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 trình nhận vượt quá phạm vi vòng đời của Ngữ cảnh. Ví dụ: điều này có thể xảy ra khi bạn đăng ký một trình thu nhận trên phạm vi Hoạt động, nhưng bạn quên huỷ đăng ký trình thu nhận đó khi hệ thống huỷ Hoạt động. Do đó, hãy luôn huỷ đăng ký broadcast receiver.

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ý trình thu nhận trong phạm vi nhỏ nhất

Bạn chỉ nên đăng ký broadcast receiver khi thực sự quan tâm đến kết quả. Chọn phạm vi bộ thu nhỏ nhất có thể:

  • Phương thức vòng đời LifecycleResumeEffect hoặc hoạt động onResume/onPause: Trình thu nhận thông báo 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.
  • Phương thức vòng đời LifecycleStartEffect hoặc hoạt động onStart/onStop: Trình thu nhận thông báo 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: Broadcast receiver 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 kết hợp. Phạm vi này không được đính kèm vào phạm vi vòng đời hoạt động. Cân nhắc việc đăng ký trình nhận trên ngữ cảnh ứng dụng. Lý do là theo lý thuyết, thành phần kết hợp 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.
  • Hoạt động onCreate/onDestroy: Trình thu phát sóng nhận được thông tin cập nhật khi hoạt động ở trạng thái đã tạo. Hãy nhớ huỷ đăng ký trong onDestroy() chứ không phải onSaveInstanceState(Bundle) vì phương thức này có thể không được gọi.
  • Phạm vi tuỳ chỉnh: Ví dụ: bạn có thể đăng ký một trình thu trong phạm vi ViewModel để trình thu này vẫn tồn tại sau khi tạo lại hoạt động. Hãy nhớ sử dụng ngữ cảnh ứng dụng để đăng ký trình nhận, vì trình 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ó trạng thái 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ý broadcast receiver bên trong một thành phần kết hợp sẽ khiến broadcast receiver đó 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 trước hiển thị cùng một nội dung khi truyền cùng một 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 đó, bạn nên chuyển việc tạo broadcast receiver ra khỏi một thành phần kết hợp để không có 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ộ thu được khai báo trong tệp kê khai

Nếu bạn khai báo broadcast receiver 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 broadcast receiver trong tệp kê khai, hãy thực hiện các bước sau:

  1. 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à trình thu nhận đăng ký.

  2. Lớp con BroadcastReceiver và triển khai onReceive(Context, Intent). Broadcast receiver trong ví dụ sau đây sẽ ghi lại 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 thu khi ứng dụng được cài đặt. Sau đó, trình thu 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à phân phố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 thời gian của lệnh 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 quy trình

Việc BroadcastReceiver của bạn có hoạt động hay không sẽ ảnh hưởng đến quy trình chứa trong đó, điều này có thể làm thay đổi khả năng tắt hệ thống. Một quy trình trên nền trước sẽ thực thi phương thức onReceive() của trình thu. Hệ thống sẽ chạy quy trình này ngoại trừ khi chịu áp lực bộ nhớ cực độ.

Hệ thống sẽ huỷ kích hoạt BroadcastReceiver sau onReceive(). Tầm quan trọng của quy trình lưu trữ của trình nhận phụ thuộc vào các thành phần ứng dụng của trình nhận. Nếu quy trình đó chỉ lưu trữ một trình thu nhận đượ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. Điều này thường xảy ra với những ứng dụng mà người dùng chưa từng hoặc gần đây chưa tương tác.

Do đó, broadcast receiver không được 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 bất cứ lúc nào sau onReceive() để thu hồi bộ nhớ, chấm dứt luồng đã tạo. Để duy trì hoạt động của quy trình, hãy lên lịch JobService từ bộ thu bằng JobScheduler để hệ thống biết quy 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.

Gửi thông báo

Android cung cấp hai cách để ứng dụng gửi thông báo truyền tin:

  • Phương thức sendOrderedBroadcast(Intent, String) gửi thông báo truyền tin đến một trình thu nhận 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. Phương thức này 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 trình thu. Để làm việc này, hãy sử dụng thuộc tính android:priority của bộ lọc ý định trùng khớp. Các trình thu nhận có cùng mức độ ưu tiên sẽ chạy theo thứ tự tuỳ ý.
  • Phương thức sendBroadcast(Intent) gửi thông báo truyền tin đến tất cả trình nhận theo thứ tự không xác định. Đây được gọi là Thông báo truyền tin thông thường. Cách này hiệu quả hơn nhưng đồng nghĩa với việc các trình thu nhận không thể đọc kết quả từ các trình thu 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 Ý định 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 truyền tin được gói trong đố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ông tin bổ sung 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 đế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 các quyền

Quyền cho phép bạn hạn chế thông báo truyền tin đến một 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 trình gửi hoặc trình nhận của một thông báo truyền tin.

Gửi thông báo truyền tin 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 trình nhận đã 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 đó nguy hiểm, bạn phải cấp quyền trước khi broadcast receiver có thể nhận được thông báo truyền tin. Ví dụ: mã sau đây sẽ 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 chung về quyền và bảo mật, hãy xem phần Quyền hệ thống.

Nhận thông báo truyền tin có quyền

Nếu bạn chỉ định một tham số quyền khi đăng ký trình thu nhận thông báo truyền tin (bằng thẻ registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) hoặc trong thẻ <receiver> trong tệp kê khai), thì chỉ những trình truyền tin đã yêu cầu quyền bằng thẻ <uses-permission> trong tệp kê khai mới có thể gửi Ý định đến trình thu nhận. Nếu 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 trình 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 các 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 truyền tin:

  • 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 tính nă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ộ thu được đăng ký theo bối cảnh. Ví dụ: thông báo truyền tin CONNECTIVITY_ACTION chỉ được phân phối đến các trình thu nhận đã đăng ký theo ngữ cảnh.

  • Khô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ếu ứng dụng đó đăng ký nhận thông báo truyền tin. Có 3 cách để kiểm soát những người có thể nhận thông báo truyền tin 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 (API cấp 14) trở lên, bạn có thể chỉ định một gói bằng setPackage(String) khi gửi thông báo truyền tin. Hệ thống sẽ 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 trình thu nhận, mọi ứng dụng đều có thể gửi thông báo truyền tin có thể độc hại đến trình thu nhận của ứng dụng. Có một số cách để giới hạn số lượng 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 trình nhận đượ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. Broadcast 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 hành động truyền tin là toàn cục. Đảm bảo rằng tên thao tác 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 trình nhận chạy trên luồng chính, nên phương thức này sẽ thực thi và trả về nhanh chóng. Nếu bạn cần thực hiện công việc lâu dài, hãy cẩn thận khi tạo luồng hoặc bắt đầu dịch vụ trong nền vì hệ thống có thể tắt toàn bộ quy trình sau khi onReceive() trả về. Để biết thêm thông tin, hãy xem phần Tác động đến trạng thái quy trình. Để thực hiện công việc chạy trong thời gian dài, bạn nên:

    • Gọi goAsync() trong phương thức onReceive() của trình nhận và truyền BroadcastReceiver.PendingResult đến luồng nền. Điều này giúp thông báo truyền tin vẫn hoạt động sau khi 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). Phương thức này cho phép bạn di chuyển công việc sang một luồng khác để tránh làm gián đoạn 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 Lên lịch công việc thông minh.
  • Đừng bắt đầu 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 broadcast receiver. Thay vào đó, hãy cân nhắc việc hiển thị một thông báo.