ภาพรวมการออกอากาศ

แอป Android จะส่งและรับข้อความประกาศจากระบบ Android และ แอป Android อื่นๆ ซึ่งคล้ายกับรูปแบบการออกแบบการเผยแพร่-ติดตาม โดยปกติแล้วระบบและแอปจะส่งประกาศเมื่อเกิดเหตุการณ์บางอย่าง ตัวอย่างเช่น ระบบ Android จะส่งประกาศเมื่อเกิดเหตุการณ์ต่างๆ ของระบบ เช่น การบูตระบบหรือการชาร์จอุปกรณ์ นอกจากนี้ แอปยังส่งประกาศที่กำหนดเองด้วย เช่น เพื่อแจ้งให้แอปอื่นๆ ทราบถึงสิ่งที่อาจเป็นที่สนใจ (เช่น การดาวน์โหลดข้อมูลใหม่)

แอปสามารถลงทะเบียนเพื่อรับประกาศที่เฉพาะเจาะจงได้ เมื่อมีการส่งประกาศ ระบบจะกำหนดเส้นทางประกาศไปยังแอปที่สมัครรับข้อมูลเพื่อรับประกาศประเภทนั้นโดยอัตโนมัติ

โดยทั่วไปแล้ว คุณสามารถใช้ประกาศเป็นระบบการรับส่งข้อความในแอปและนอกโฟลว์ผู้ใช้ปกติได้ อย่างไรก็ตาม คุณต้องระมัดระวังไม่ให้ใช้โอกาสในการตอบสนองต่อประกาศและเรียกใช้งานในเบื้องหลังในทางที่ผิด ซึ่งอาจทำให้ประสิทธิภาพของระบบช้าลง

เกี่ยวกับการประกาศของระบบ

ระบบจะส่งประกาศโดยอัตโนมัติเมื่อเกิดเหตุการณ์ต่างๆ ของระบบ เช่น เมื่อระบบเปลี่ยนเข้าและออกจากโหมดเครื่องบิน แอปที่สมัครรับข้อมูลทั้งหมดจะได้รับประกาศเหล่านี้

ออบเจ็กต์ Intent จะห่อหุ้มข้อความประกาศ สตริง action จะระบุ เหตุการณ์ที่เกิดขึ้น เช่น android.intent.action.AIRPLANE_MODE นอกจากนี้ Intent อาจมีข้อมูลเพิ่มเติมที่รวมอยู่ในช่อง Extra ตัวอย่างเช่น Intent ของโหมดเครื่องบินมี Extra แบบบูลีนที่ระบุว่าโหมดเครื่องบินเปิดอยู่หรือไม่

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีอ่าน Intent และรับสตริงการดำเนินการจาก an Intent ได้ที่ Intent และ Intent Filter

การดำเนินการประกาศของระบบ

ดูรายการการดำเนินการประกาศของระบบทั้งหมดได้ในไฟล์ BROADCAST_ACTIONS.TXT ใน Android SDK การดำเนินการประกาศแต่ละรายการมีช่องค่าคงที่ที่เชื่อมโยงอยู่ ตัวอย่างเช่น ค่าของค่าคงที่ ACTION_AIRPLANE_MODE_CHANGED คือ android.intent.action.AIRPLANE_MODE เอกสารประกอบสำหรับการดำเนินการประกาศแต่ละรายการมีอยู่ในช่องค่าคงที่ที่เชื่อมโยง

การเปลี่ยนแปลงการประกาศของระบบ

แพลตฟอร์ม Android มีการพัฒนาอยู่เสมอ จึงมีการเปลี่ยนแปลงลักษณะการทำงานของการประกาศของระบบเป็นระยะๆ โปรดคำนึงถึงการเปลี่ยนแปลงต่อไปนี้เพื่อรองรับ Android ทุกเวอร์ชัน

Android 16

ใน Android 16 ไม่รับประกันลำดับการส่งประกาศโดยใช้แอตทริบิวต์ android:priority หรือ IntentFilter.setPriority() ในกระบวนการต่างๆ ระบบจะพิจารณาลำดับความสำคัญของการประกาศภายในกระบวนการของแอปพลิเคชันเดียวกันเท่านั้น ไม่ใช่ในกระบวนการทั้งหมด

นอกจากนี้ ระบบจะจำกัดลำดับความสำคัญของการประกาศไว้ในช่วง (SYSTEM_LOW_PRIORITY + 1, SYSTEM_HIGH_PRIORITY - 1) โดยอัตโนมัติ เฉพาะคอมโพเนนต์ของระบบเท่านั้นที่ได้รับอนุญาตให้ตั้งค่า SYSTEM_LOW_PRIORITY, SYSTEM_HIGH_PRIORITY เป็นลำดับความสำคัญของการประกาศ

Android 14

ขณะที่แอปอยู่ในสถานะแคช ระบบจะเพิ่มประสิทธิภาพการส่งประกาศ เพื่อรักษาสถานะของระบบให้ดี ตัวอย่างเช่น ระบบจะเลื่อนการประกาศของระบบที่มีความสำคัญน้อยกว่า เช่น ACTION_SCREEN_ON ขณะที่แอปอยู่ในสถานะแคช เมื่อแอปเปลี่ยนจากสถานะแคชไปเป็นวงจรการทำงานของกระบวนการที่ ใช้งานอยู่, ระบบจะส่งประกาศที่เลื่อนไว้

ประกาศที่สำคัญซึ่งประกาศไว้ในไฟล์ Manifest จะนำแอปออกจากสถานะแคชชั่วคราว เพื่อส่งประกาศ

Android 9

เริ่มตั้งแต่ Android 9 (ระดับ API 28) เป็นต้นไป การNETWORK_STATE_CHANGED_ACTION ประกาศจะไม่ได้รับข้อมูลเกี่ยวกับตำแหน่งของผู้ใช้หรือข้อมูลที่ระบุตัวบุคคลได้

หากแอปของคุณติดตั้งอยู่ในอุปกรณ์ที่ใช้ Android 9.0 (ระดับ API 28) ขึ้นไป ระบบจะไม่รวม SSID, BSSID, ข้อมูลการเชื่อมต่อ หรือผลการสแกนในการประกาศ Wi-Fi หากต้องการรับข้อมูลนี้ ให้เรียกใช้ getConnectionInfo() แทน

Android 8.0

เริ่มตั้งแต่ Android 8.0 (ระดับ API 26) เป็นต้นไป ระบบจะกำหนดข้อจำกัดเพิ่มเติมกับตัวรับสัญญาณที่ประกาศไว้ในไฟล์ Manifest

หากแอปกำหนดเป้าหมายเป็น Android 8.0 ขึ้นไป คุณจะใช้ไฟล์ Manifest เพื่อ ประกาศตัวรับสัญญาณสำหรับการประกาศ _โดยนัย_ ส่วนใหญ่ไม่ได้ (การประกาศที่ไม่ได้กำหนดเป้าหมายเป็น แอปของคุณโดยเฉพาะ) แต่คุณยังคงใช้ตัวรับสัญญาณที่ลงทะเบียนตามบริบท ได้เมื่อผู้ใช้ใช้แอปของคุณอยู่

Android 7.0

Android 7.0 (ระดับ API 24) ขึ้นไปจะไม่ส่งประกาศของระบบต่อไปนี้

นอกจากนี้ แอปที่กำหนดเป้าหมายเป็น Android 7.0 ขึ้นไปต้องลงทะเบียนการประกาศ CONNECTIVITY_ACTION โดยใช้ registerReceiver(BroadcastReceiver, IntentFilter) การประกาศตัวรับสัญญาณในไฟล์ Manifest จะใช้ไม่ได้

รับประกาศ

แอปสามารถรับประกาศได้ 2 วิธี ได้แก่ ผ่านตัวรับสัญญาณที่ลงทะเบียนตามบริบทและตัวรับสัญญาณที่ประกาศไว้ในไฟล์ Manifest

ตัวรับสัญญาณที่ลงทะเบียนตามบริบท

ตัวรับสัญญาณที่ลงทะเบียนตามบริบทจะรับประกาศได้ตราบใดที่บริบทการลงทะเบียนยังคงใช้ได้ ซึ่งโดยปกติแล้วจะอยู่ระหว่างการเรียกใช้ registerReceiver และ unregisterReceiver นอกจากนี้ บริบทการลงทะเบียนจะใช้ไม่ได้เมื่อระบบทำลายบริบทที่เกี่ยวข้อง ตัวอย่างเช่น หากคุณลงทะเบียนภายใน บริบท Activity คุณจะได้รับประกาศตราบใดที่กิจกรรม ยังคงใช้งานอยู่ หากคุณลงทะเบียนด้วยบริบทแอปพลิเคชัน คุณจะได้รับประกาศตราบใดที่แอปทำงานอยู่

หากต้องการลงทะเบียนตัวรับสัญญาณด้วยบริบท ให้ทำตามขั้นตอนต่อไปนี้

  1. ในไฟล์ Build ระดับโมดูลของแอป ให้รวมไลบรารี AndroidX Core เวอร์ชัน 1.9.0 ขึ้นไป:

    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")
    }
  2. สร้างอินสแตนซ์ของ BroadcastReceiver:

    Kotlin

    val myBroadcastReceiver = MyBroadcastReceiver()
    

    Java

    MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
    
  3. สร้างอินสแตนซ์ของ IntentFilter:

    Kotlin

    val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
    

    Java

    IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");
    
  4. เลือกว่าควรส่งออกและแสดง Broadcast Receiver ให้แอปอื่นๆ ในอุปกรณ์เห็นหรือไม่ หากตัวรับสัญญาณนี้กำลังรอรับประกาศที่ส่งจากระบบหรือจากแอปอื่นๆ แม้แต่แอปอื่นๆ ที่คุณเป็นเจ้าของ ให้ใช้แฟล็ก RECEIVER_EXPORTED แต่หากตัวรับสัญญาณนี้กำลังรอรับเฉพาะประกาศที่ส่งโดยแอปของคุณ ให้ใช้แฟล็ก 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. ลงทะเบียนตัวรับสัญญาณโดยเรียกใช้ registerReceiver()

    Kotlin

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

    Java

    ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);
    
  6. หากต้องการหยุดรับประกาศ ให้เรียกใช้ unregisterReceiver(android.content.BroadcastReceiver) อย่าลืมยกเลิกการลงทะเบียนตัวรับสัญญาณเมื่อไม่ต้องการใช้อีกต่อไปหรือบริบทใช้ไม่ได้อีกต่อไป

ยกเลิกการลงทะเบียน Broadcast Receiver ของคุณ

ขณะที่ Broadcast Receiver ลงทะเบียนอยู่ Broadcast Receiver จะเก็บข้อมูลอ้างอิงถึงบริบทที่คุณลงทะเบียนไว้ ซึ่งอาจทำให้เกิดการรั่วไหลหากขอบเขตที่ลงทะเบียนของตัวรับสัญญาณเกินขอบเขตวงจรการทำงานของบริบท ตัวอย่างเช่น ปัญหานี้อาจเกิดขึ้นเมื่อคุณลงทะเบียนตัวรับสัญญาณในขอบเขตกิจกรรม แต่ลืมยกเลิกการลงทะเบียนเมื่อระบบทำลายกิจกรรม ดังนั้น ให้ยกเลิกการลงทะเบียน 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
    }
}

ลงทะเบียนตัวรับสัญญาณในขอบเขตที่เล็กที่สุด

คุณควรลงทะเบียน Broadcast Receiver เมื่อสนใจผลลัพธ์จริงๆ เท่านั้น เลือกขอบเขตตัวรับสัญญาณที่เล็กที่สุดเท่าที่จะเป็นไปได้

  • LifecycleResumeEffect หรือเมธอดวงจรกิจกรรม onResume/onPause ของกิจกรรม: Broadcast Receiver จะได้รับการอัปเดตเฉพาะขณะที่แอปอยู่ในสถานะกลับมาทำงานต่อ
  • LifecycleStartEffect หรือเมธอดวงจรกิจกรรม onStart/onStop ของกิจกรรม: Broadcast Receiver จะได้รับการอัปเดตเฉพาะขณะที่แอปอยู่ในสถานะกลับมาทำงานต่อ
  • DisposableEffect: Broadcast Receiver จะได้รับการอัปเดตเฉพาะขณะที่ Composable ได้อยู่ในแผนผังการคอมโพส ขอบเขตนี้ไม่ได้แนบอยู่กับขอบเขตวงจรกิจกรรม ลองลงทะเบียนตัวรับสัญญาณในบริบทแอปพลิเคชัน เนื่องจากในทางทฤษฎีแล้ว คอมโพสได้อาจมีอายุการใช้งานยาวนานกว่าขอบเขตวงจรกิจกรรมและทำให้เกิดการรั่วไหลของกิจกรรม
  • onCreate/onDestroy ของกิจกรรม: Broadcast Receiver จะได้รับการอัปเดตขณะที่กิจกรรมอยู่ในสถานะสร้าง อย่าลืมยกเลิกการลงทะเบียนใน onDestroy() ไม่ใช่ onSaveInstanceState(Bundle) เนื่องจากระบบอาจไม่เรียกใช้เมธอดนี้
  • ขอบเขตที่กำหนดเอง: ตัวอย่างเช่น คุณสามารถลงทะเบียนตัวรับสัญญาณในขอบเขต ViewModel เพื่อให้ตัวรับสัญญาณยังคงอยู่ได้แม้จะมีการสร้างกิจกรรมขึ้นใหม่ อย่าลืมใช้บริบทแอปพลิเคชันเพื่อลงทะเบียนตัวรับสัญญาณ เนื่องจากตัวรับสัญญาณอาจมีอายุการใช้งานยาวนานกว่าขอบเขตวงจรการทำงานของกิจกรรมและทำให้เกิดการรั่วไหลของกิจกรรม

สร้างคอมโพสได้แบบมีสถานะและไม่มีสถานะ

Compose มี Composable แบบมีสถานะและไม่มีสถานะ การลงทะเบียนหรือยกเลิกการลงทะเบียน Broadcast Receiver ภายใน Composable จะทำให้ Composable มีสถานะ คอมโพสได้ไม่ใช่ฟังก์ชันที่กำหนดได้ซึ่งแสดงผลเนื้อหาเดียวกันเมื่อส่งพารามิเตอร์เดียวกัน สถานะภายในอาจเปลี่ยนแปลงได้ตามการเรียกใช้ตัวรับสัญญาณประกาศที่ลงทะเบียนไว้

แนวทางปฏิบัติแนะนำใน Compose คือการแยก Composable ออกเป็นเวอร์ชันแบบมีสถานะและไม่มีสถานะ ดังนั้น เราขอแนะนำให้คุณยกการสร้าง Broadcast Receiver ออกจาก Composable เพื่อให้ Broadcast Receiver ไม่มีสถานะ

@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
}

ตัวรับสัญญาณที่ประกาศไว้ในไฟล์ Manifest

หากคุณประกาศ Broadcast Receiver ในไฟล์ Manifest ระบบจะเปิดแอปเมื่อมีการส่งประกาศ หากแอปยังไม่ได้ทำงาน ระบบจะเปิดแอป

หากต้องการประกาศ Broadcast Receiver ในไฟล์ Manifest ให้ทำตามขั้นตอนต่อไปนี้

  1. ระบุองค์ประกอบ <receiver> ในไฟล์ Manifest ของแอป

    <!-- 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>
    

    Intent Filter จะระบุการดำเนินการประกาศที่ตัวรับสัญญาณสมัครรับข้อมูล

  2. สร้างคลาสย่อย BroadcastReceiver และใช้ onReceive(Context, Intent) Broadcast Receiver ในตัวอย่างต่อไปนี้จะบันทึกและแสดงเนื้อหาของประกาศ

    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); }
            }
        }
    }
    

โปรแกรมจัดการแพ็กเกจของระบบจะลงทะเบียนตัวรับสัญญาณเมื่อมีการติดตั้งแอป จากนั้นตัวรับสัญญาณจะกลายเป็นจุดเริ่มต้นที่แยกต่างหากในแอป ซึ่งหมายความว่าระบบจะเริ่มแอปและส่งประกาศได้หากแอปไม่ได้ทำงานอยู่

ระบบจะสร้างออบเจ็กต์คอมโพเนนต์ BroadcastReceiver ใหม่เพื่อจัดการ ประกาศแต่ละรายการที่ได้รับ ออบเจ็กต์นี้จะใช้ได้เฉพาะในระยะเวลาของ การเรียกใช้ onReceive(Context, Intent) เมื่อโค้ดของคุณกลับมาจากเมธอดนี้ ระบบจะถือว่าคอมโพเนนต์ไม่ได้ใช้งานอีกต่อไป

ผลกระทบต่อสถานะกระบวนการ

การทำงานของ BroadcastReceiver จะส่งผลต่อกระบวนการที่มีตัวรับสัญญาณอยู่ ซึ่งอาจเปลี่ยนโอกาสที่ระบบจะหยุดกระบวนการ กระบวนการที่ทำงานอยู่เบื้องหน้าจะเรียกใช้เมธอด onReceive() ของตัวรับสัญญาณ ระบบจะเรียกใช้กระบวนการดังกล่าว ยกเว้นในกรณีที่หน่วยความจำใกล้เต็ม

ระบบจะปิดใช้งาน BroadcastReceiver หลังจาก onReceive() ความสำคัญของกระบวนการโฮสต์ของตัวรับสัญญาณจะขึ้นอยู่กับคอมโพเนนต์ของแอป หากกระบวนการนั้นโฮสต์เฉพาะตัวรับสัญญาณที่ประกาศไว้ในไฟล์ Manifest ระบบอาจหยุดกระบวนการหลังจาก onReceive() เพื่อเพิ่มทรัพยากรให้กับกระบวนการอื่นๆ ที่สำคัญกว่า ซึ่งมักเกิดขึ้นกับแอปที่ผู้ใช้ไม่เคยโต้ตอบด้วยหรือไม่ได้โต้ตอบด้วยเมื่อเร็วๆ นี้

ดังนั้น ตัวรับสัญญาณประกาศจึงไม่ควรเริ่มเทรดเบื้องหลังที่ทำงานเป็นเวลานาน ระบบสามารถหยุดกระบวนการได้ทุกเมื่อหลังจาก onReceive() เพื่อเรียกคืนหน่วยความจำ ซึ่งจะยุติเทรดที่สร้างขึ้น หากต้องการให้กระบวนการทำงานอยู่ ให้กำหนดเวลา JobService จากตัวรับสัญญาณโดยใช้ JobScheduler เพื่อให้ระบบทราบว่ากระบวนการยังคงทำงานอยู่ ภาพรวมของงานเบื้องหลัง ดูรายละเอียดเพิ่มเติมได้ที่

ส่งประกาศ

Android มี 2 วิธีให้แอปส่งประกาศ

  • เมธอด sendOrderedBroadcast(Intent, String) จะส่งประกาศไปยัง ตัวรับสัญญาณทีละรายการ เมื่อตัวรับสัญญาณแต่ละรายการทำงานตามลำดับ ตัวรับสัญญาณจะส่งต่อผลลัพธ์ไปยังตัวรับสัญญาณถัดไปได้ นอกจากนี้ ตัวรับสัญญาณยังยกเลิกประกาศโดยสมบูรณ์ได้ เพื่อไม่ให้ประกาศไปถึงตัวรับสัญญาณอื่นๆ คุณสามารถควบคุมลำดับที่ตัวรับสัญญาณทำงานภายในกระบวนการของแอปเดียวกันได้ โดยใช้แอตทริบิวต์ android:priority ของ Intent Filter ที่ตรงกัน ตัวรับสัญญาณที่มีลำดับความสำคัญเดียวกันจะทำงานตามลำดับที่กำหนด
  • เมธอด sendBroadcast(Intent) จะส่งประกาศไปยังตัวรับสัญญาณทั้งหมด ตามลำดับที่ ไม่ได้กำหนด ซึ่งเรียกว่าการประกาศปกติ วิธีนี้มีประสิทธิภาพมากกว่า แต่หมายความว่าตัวรับสัญญาณจะอ่านผลลัพธ์จากตัวรับสัญญาณอื่นๆ ส่งต่อข้อมูลที่ได้รับจากการประกาศ หรือยกเลิกประกาศไม่ได้

ข้อมูลโค้ดต่อไปนี้แสดงวิธีส่งประกาศโดยการสร้าง Intent และเรียกใช้ 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);

ข้อความประกาศจะรวมอยู่ในออบเจ็กต์ Intent สตริง action ของ Intent ต้องระบุไวยากรณ์ชื่อแพ็กเกจ Java ของแอปและระบุเหตุการณ์ประกาศที่ไม่ซ้ำกัน คุณสามารถแนบข้อมูลเพิ่มเติมกับ Intent ได้ด้วย putExtra(String, Bundle) นอกจากนี้ คุณยังจำกัดการประกาศให้เฉพาะ แอปบางแอปในองค์กรเดียวกันได้โดยเรียกใช้ setPackage(String) ใน Intent

จำกัดการประกาศด้วยสิทธิ์

สิทธิ์ช่วยให้คุณจำกัดการประกาศให้เฉพาะแอปบางแอปที่มีสิทธิ์บางอย่าง คุณสามารถบังคับใช้ข้อจำกัดกับผู้ส่งหรือผู้รับประกาศก็ได้

ส่งประกาศด้วยสิทธิ์

เมื่อเรียกใช้ sendBroadcast(Intent, String) หรือ sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) คุณสามารถระบุพารามิเตอร์สิทธิ์ได้ เฉพาะตัวรับสัญญาณที่ขอสิทธิ์ดังกล่าวด้วยแท็ก <uses-permission> ในไฟล์ Manifest เท่านั้นที่จะรับประกาศได้ หากสิทธิ์เป็นสิทธิ์ที่เป็นอันตราย คุณต้องให้สิทธิ์ก่อนตัวรับสัญญาณจึงจะรับประกาศได้ ตัวอย่างเช่น โค้ดต่อไปนี้จะส่งประกาศพร้อมสิทธิ์

Kotlin

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION)

Java

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);

หากต้องการรับประกาศ แอปที่รับต้องขอสิทธิ์ดังนี้

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

คุณสามารถระบุสิทธิ์ของระบบที่มีอยู่ เช่น BLUETOOTH_CONNECT หรือกำหนดสิทธิ์ที่กำหนดเองด้วยองค์ประกอบ <permission> ดูข้อมูลเกี่ยวกับสิทธิ์และความปลอดภัยโดยทั่วไปได้ที่ สิทธิ์ของระบบ

รับประกาศด้วยสิทธิ์

หากคุณระบุพารามิเตอร์สิทธิ์เมื่อลงทะเบียน Broadcast Receiver (ด้วย registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) หรือใน <receiver> แท็กในไฟล์ Manifest) เฉพาะผู้ประกาศที่ขอสิทธิ์ด้วยแท็ก <uses-permission> ในไฟล์ Manifest เท่านั้นที่จะส่ง Intent ไปยัง Broadcast Receiver ได้ หากสิทธิ์เป็นสิทธิ์ที่เป็นอันตราย ผู้ประกาศต้องได้รับสิทธิ์ด้วย

ตัวอย่างเช่น สมมติว่าแอปที่รับมีตัวรับสัญญาณที่ประกาศไว้ในไฟล์ Manifest ดังนี้

<!-- 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>

หรือแอปที่รับมีตัวรับสัญญาณที่ลงทะเบียนตามบริบทดังนี้

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
);

จากนั้นแอปที่ส่งต้องขอสิทธิ์ดังนี้จึงจะส่งประกาศไปยังตัวรับสัญญาณเหล่านั้นได้

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

ข้อควรพิจารณาด้านความปลอดภัย

ข้อควรพิจารณาด้านความปลอดภัยบางประการสำหรับการส่งและรับประกาศมีดังนี้

  • หากมีแอปจำนวนมากที่ลงทะเบียนเพื่อรับประกาศเดียวกันในไฟล์ Manifest อาจทำให้ระบบเปิดแอปจำนวนมาก ซึ่งส่งผลกระทบอย่างมากต่อทั้งประสิทธิภาพของอุปกรณ์และประสบการณ์ของผู้ใช้ หากต้องการหลีกเลี่ยงปัญหานี้ ให้ใช้การลงทะเบียนตามบริบทแทนการประกาศในไฟล์ Manifest บางครั้งระบบ Android เองก็บังคับให้ใช้ตัวรับสัญญาณที่ลงทะเบียนตามบริบท ตัวอย่างเช่น ระบบจะส่งประกาศ CONNECTIVITY_ACTION ไปยัง ตัวรับสัญญาณที่ลงทะเบียนตามบริบทเท่านั้น

  • อย่าประกาศข้อมูลที่ละเอียดอ่อนโดยใช้ Intent แบบไม่เจาะจงปลายทาง แอปใดๆ ก็อ่านข้อมูลได้หากลงทะเบียนเพื่อรับประกาศ คุณควบคุมผู้ที่รับประกาศได้ 3 วิธีดังนี้

    • คุณสามารถระบุสิทธิ์เมื่อส่งประกาศ
    • ใน Android 4.0 (ระดับ API 14) ขึ้นไป คุณสามารถระบุa แพ็กเกจ ด้วย setPackage(String) เมื่อส่งประกาศ ระบบจะจำกัดการประกาศให้เฉพาะแอปบางแอปที่ตรงกับแพ็กเกจ
  • เมื่อคุณลงทะเบียนตัวรับสัญญาณ แอปใดๆ ก็ส่งประกาศที่อาจเป็นอันตรายไปยังตัวรับสัญญาณของแอปคุณได้ คุณจำกัดประกาศที่แอปได้รับได้หลายวิธีดังนี้

    • คุณสามารถระบุสิทธิ์เมื่อลงทะเบียน Broadcast Receiver
    • สำหรับตัวรับสัญญาณที่ประกาศไว้ในไฟล์ Manifest คุณสามารถตั้งค่าแอตทริบิวต์ android:exported เป็น "false" ในไฟล์ Manifest ตัวรับสัญญาณจะไม่รับประกาศจากแหล่งที่มาภายนอกแอป
  • เนมสเปซสำหรับการดำเนินการประกาศเป็นแบบส่วนกลาง ตรวจสอบว่าชื่อการดำเนินการและสตริงอื่นๆ เขียนอยู่ในเนมสเปซที่คุณเป็นเจ้าของ มิฉะนั้น คุณอาจเกิดความขัดแย้งกับแอปอื่นๆ โดยไม่ตั้งใจ

  • เนื่องจากเมธอด onReceive(Context, Intent) ของตัวรับสัญญาณทำงานในเทรดหลัก เมธอดจึงควรดำเนินการและกลับมาทำงานต่ออย่างรวดเร็ว หากคุณต้องทำงานที่ใช้เวลานาน ให้ระมัดระวังในการสร้างเทรดหรือเริ่มบริการเบื้องหลัง เนื่องจากระบบสามารถหยุดกระบวนการทั้งหมดได้หลังจาก onReceive() กลับมาทำงานต่อ ดูข้อมูลเพิ่มเติมได้ที่ ผลกระทบต่อสถานะกระบวนการ หากต้องการทำงานที่ใช้เวลานาน เราขอแนะนำให้ทำดังนี้

    • เรียกใช้ goAsync() ในเมธอด onReceive() ของตัวรับสัญญาณและ ส่ง BroadcastReceiver.PendingResult ไปยังเทรดเบื้องหลัง การดำเนินการนี้จะทำให้ประกาศยังคงใช้งานอยู่หลังจากกลับมาจาก onReceive() อย่างไรก็ตาม แม้จะใช้วิธีนี้ ระบบก็คาดหวังให้คุณดำเนินการกับประกาศให้เสร็จอย่างรวดเร็ว (ภายใน 10 วินาที) แต่ระบบอนุญาตให้คุณย้ายงานไปยังเทรดอื่นเพื่อหลีกเลี่ยงไม่ให้เทรดหลักเกิดข้อผิดพลาด
    • กำหนดเวลาการเรียกใช้งานด้วย JobScheduler ดูข้อมูลเพิ่มเติมได้ที่ ดู การกำหนดเวลาการเรียกใช้งานอัจฉริยะ
  • อย่าเริ่มกิจกรรมจากตัวรับสัญญาณประกาศ เนื่องจากประสบการณ์ของผู้ใช้จะสะดุด โดยเฉพาะอย่างยิ่งหากมีตัวรับสัญญาณมากกว่า 1 รายการ ให้พิจารณา แสดงการแจ้งเตือนแทน