نظرة عامة على عمليات البث

ترسل تطبيقات Android رسائل البث وتتلقّاها من نظام Android و تطبيقات Android الأخرى، على غرار نمط تصميم النشر والاشتراك. يرسل النظام والتطبيقات عادةً عمليات البث عند وقوع أحداث معيّنة. على سبيل المثال، يرسل نظام Android عمليات البث عند وقوع أحداث مختلفة في النظام، مثل بدء تشغيل النظام أو شحن الجهاز. ترسل التطبيقات أيضًا عمليات بث مخصّصة، مثلاً لإشعار التطبيقات الأخرى بشيء قد يهمّها (مثل تنزيل بيانات جديدة).

يمكن للتطبيقات التسجيل لتلقّي عمليات بث معيّنة. عند إرسال عملية بث، يوجّه النظام تلقائيًا عمليات البث إلى التطبيقات التي اشتركت في تلقّي هذا النوع من عمليات البث.

بشكل عام، يمكن استخدام عمليات البث كنظام مراسلة بين التطبيقات وخارج مسار المستخدم العادي. ومع ذلك، يجب توخي الحذر من إساءة استخدام فرصة الردّ على عمليات البث وتشغيل المهام في الخلفية التي يمكن أن تساهم في بطء أداء النظام.

لمحة عن رسائل البث من النظام

يرسل النظام تلقائيًا عمليات البث عند وقوع أحداث مختلفة في النظام، مثل تبديل النظام بين "وضع الطائرة" وخارجه. تتلقّى جميع التطبيقات المشترِكة عمليات البث هذه.

يحتوي عنصر Intent على الإعلان على جميع الأجهزة. يحدّد السلسلة action الحدث الذي وقع، مثل android.intent.action.AIRPLANE_MODE. قد يتضمّن الغرض أيضًا معلومات إضافية مضمّنة في الحقل الإضافي. على سبيل المثال، يتضمّن الغرض من "وضع الطائرة" حقل إضافي منطقي يشير إلى ما إذا كان "وضع الطائرة" مفعّلاً أم لا.

لمزيد من المعلومات حول كيفية قراءة intents والحصول على سلسلة الإجراءات من intent ، يُرجى الاطّلاع على intents وintent filters.

إجراءات البث من النظام

للاطّلاع على قائمة كاملة بإجراءات البث من النظام، يُرجى الرجوع إلى ملف 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، أثناء وجود التطبيق في حالة التخزين المؤقت. بمجرد انتقال التطبيق من حالة التخزين المؤقت إلى دورة حياة عملية نشطة، يسلّم النظام أي عمليات بث مؤجّلة.

تؤدي عمليات البث المهمة التي يتم تعريفها في ملف البيان إلى إزالة التطبيقات مؤقتًا من حالة التخزين المؤقت لتسليمها.

Android 9

اعتبارًا من Android 9 (مستوى واجهة برمجة التطبيقات 28)، لا يتلقّى البث NETWORK_STATE_CHANGED_ACTION معلومات عن موقع المستخدم أو البيانات التي تكشف الهوية الشخصية.

إذا كان تطبيقك مثبّتًا على جهاز يعمل بنظام Android 9.0 (المستوى 28 من واجهة برمجة التطبيقات) أو إصدار أعلى، لا يتضمّن النظام معرّفات SSID أو معرّفات BSSID أو معلومات الاتصال أو نتائج الفحص في عمليات بث Wi-Fi. للحصول على هذه المعلومات، استخدِم getConnectionInfo() بدلاً من ذلك.

Android 8.0

اعتبارًا من Android 8.0 (مستوى واجهة برمجة التطبيقات 26)، يفرض النظام قيودًا إضافية على أجهزة الاستقبال التي تم تعريفها في ملف البيان.

إذا كان تطبيقك يستهدف Android 8.0 أو إصدار أعلى، لا يمكنك استخدام ملف البيان لـ تعريف جهاز استقبال لمعظم عمليات البث _الضمنية_ (عمليات البث التي لا تستهدف تطبيقك تحديدًا). سيظل بإمكانك استخدام جهاز استقبال مسجَّل في السياق عندما يستخدم المستخدم تطبيقك بنشاط.

Android 7.0

لا يرسل Android 7.0 (المستوى 24 من واجهة برمجة التطبيقات) والإصدارات الأحدث عمليات البث التالية من النظام:

أيضًا، يجب أن تسجّل التطبيقات التي تستهدف Android 7.0 والإصدارات الأحدث عملية البث CONNECTIVITY_ACTION باستخدام registerReceiver(BroadcastReceiver, IntentFilter). لا ينجح تعريف جهاز استقبال في ملف البيان.

تلقّي عمليات البث

يمكن للتطبيقات تلقّي عمليات البث بطريقتَين: من خلال أجهزة الاستقبال المسجَّلة في السياق وأجهزة الاستقبال التي تم تعريفها في ملف البيان.

أجهزة الاستقبال المسجَّلة في السياق

تتلقّى أجهزة الاستقبال المسجَّلة في السياق عمليات البث طالما أنّ سياق التسجيل صالح. ويكون ذلك عادةً بين استدعاءات registerReceiver و unregisterReceiver. يصبح سياق التسجيل غير صالح أيضًا عندما يدمّر النظام السياق المقابل. على سبيل المثال، إذا سجّلت في سياق Activity، ستتلقّى عمليات البث طالما أنّ النشاط يظل نشطًا. إذا سجّلت باستخدام سياق التطبيق، ستتلقّى عمليات البث طالما أنّ التطبيق قيد التشغيل.

لتسجيل جهاز استقبال في سياق، اتّبِع الخطوات التالية:

  1. في ملف الإصدار على مستوى الوحدة في تطبيقك، أدرِج الإصدار 1.9.0 أو إصدار أعلى من مكتبة AndroidX Core:

    Groovy

    dependencies {
        def core_version = "1.19.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.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.19.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.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. اختَر ما إذا كان يجب تصدير جهاز استقبال البث وجعله مرئيًا للتطبيقات الأخرى على الجهاز. إذا كان جهاز الاستقبال هذا يستمع إلى عمليات البث المُرسَلة من النظام أو من تطبيقات أخرى، حتى التطبيقات الأخرى التي تملكها، استخدِم العلامة 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). احرص على إلغاء تسجيل جهاز الاستقبال عندما لا تعود بحاجة إليه أو عندما يصبح السياق غير صالح.

إلغاء تسجيل جهاز استقبال البث

أثناء تسجيل جهاز استقبال البث، يحتفظ الجهاز بمرجع إلى السياق الذي سجّلت الجهاز به. قد يؤدي ذلك إلى حدوث تسرّبات إذا كان النطاق المسجَّل لجهاز الاستقبال يتجاوز نطاق دورة حياة السياق. على سبيل المثال، يمكن أن يحدث ذلك عند تسجيل جهاز استقبال في نطاق `Activity`، ولكنك تنسى إلغاء تسجيله عندما يدمّر النظام `Activity`. لذلك، عليك دائمًا إلغاء تسجيل جهاز استقبال البث.

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

تسجيل أجهزة الاستقبال في أصغر نطاق

يجب تسجيل جهاز استقبال البث فقط عندما تكون مهتمًا بالنتيجة. اختَر أصغر نطاق ممكن لجهاز الاستقبال:

  • LifecycleResumeEffect أو طرق دورة حياة onResume/onPause في النشاط: لا يتلقّى جهاز استقبال البث التحديثات إلا أثناء وجود التطبيق في حالة الاستئناف.
  • LifecycleStartEffect أو طرق دورة حياة onStart/onStop في النشاط: لا يتلقّى جهاز استقبال البث التحديثات إلا أثناء وجود التطبيق في حالة الاستئناف.
  • DisposableEffect: لا يتلقّى جهاز استقبال البث التحديثات إلا أثناء وجود العنصر المركّب في شجرة التركيب. لا يرتبط هذا النطاق بنطاق مراحل النشاط. ننصحك بتسجيل جهاز الاستقبال في سياق التطبيق. وذلك لأنّه من الناحية النظرية، يمكن أن يعيش العنصر المركّب أطول من نطاق دورة حياة النشاط ويؤدي إلى تسرّب النشاط.
  • onCreate/onDestroy في النشاط: يتلقّى جهاز استقبال البث التحديثات أثناء وجود النشاط في حالة الإنشاء. احرص على إلغاء التسجيل في onDestroy() وليس onSaveInstanceState(Bundle) لأنّه قد لا يتم استدعاء هذا الإجراء.
  • نطاق مخصّص: على سبيل المثال، يمكنك تسجيل جهاز استقبال في نطاق ViewModel، ما يجعله يبقى بعد إعادة إنشاء النشاط. احرص على استخدام سياق التطبيق لتسجيل جهاز الاستقبال، لأنّ جهاز الاستقبال يمكن أن يعيش أطول من نطاق دورة حياة النشاط ويؤدي إلى تسرّب النشاط.

إنشاء دالة مركّبة ذات حالة ودالة مركّبة بدون حالة

يحتوي Compose على عناصر مركّبة ذات حالة وأخرى بدون حالة. يؤدي تسجيل جهاز استقبال البث أو إلغاء تسجيله داخل عنصر مركّب إلى جعله ذا حالة. العنصر المركّب ليس دالة حتمية تعرض المحتوى نفسه عند تمرير المعلمات نفسها. يمكن أن تتغير الحالة الداخلية استنادًا إلى استدعاءات جهاز استقبال البث المسجَّل.

كأفضل ممارسة في Compose، ننصحك بتقسيم العناصر المركّبة إلى إصدارات ذات حالة وأخرى بدون حالة. لذلك، ننصحك برفع إنشاء جهاز استقبال البث خارج العنصر `Composable` لجعله بدون حالة:

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

أجهزة الاستقبال التي تم تعريفها في ملف البيان

إذا عرّفت جهاز استقبال بث في ملف البيان، سيشغّل النظام تطبيقك عند إرسال البث. إذا لم يكن التطبيق قيد التشغيل، سيشغّله النظام.

لتعريف جهاز استقبال بث في ملف البيان، اتّبِع الخطوات التالية:

  1. حدِّد العنصر <receiver> في ملف بيان تطبيقك.

    <!-- 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). يسجّل جهاز استقبال البث في المثال التالي محتويات البث ويعرضها:

    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(). تعتمد أهمية عملية المضيف لجهاز الاستقبال على مكوّنات التطبيق. إذا كانت هذه العملية تستضيف جهاز استقبال تم تعريفه في ملف البيان فقط، قد يوقفها النظام بعد onReceive() لتحرير الموارد للعمليات الأخرى الأكثر أهمية. ويحدث ذلك عادةً للتطبيقات التي لم يتفاعل معها المستخدم مطلقًا أو لم يتفاعل معها مؤخرًا.

وبالتالي، يجب ألا تبدأ أجهزة استقبال البث سلاسل تعليمات طويلة الأمد في الخلفية. يمكن للنظام إيقاف العملية في أي لحظة بعد onReceive() لاستعادة الذاكرة، ما يؤدي إلى إنهاء سلسلة التعليمات التي تم إنشاؤها. لإبقاء العملية نشطة، جدوِل JobService من جهاز الاستقبال باستخدام JobScheduler حتى يعرف النظام أنّ العملية لا تزال قيد التشغيل. تقدّم نظرة عامة على العمل في الخلفية مزيدًا من التفاصيل.

إرسال عمليات البث

يوفّر Android طريقتَين للتطبيقات لإرسال عمليات البث:

  • ترسل الطريقة sendOrderedBroadcast(Intent, String) عمليات البث إلى جهاز استقبال واحد في كل مرة. أثناء تنفيذ كل جهاز استقبال بدوره، يمكنه نشر نتيجة إلى جهاز الاستقبال التالي. يمكنه أيضًا إيقاف عملية البث تمامًا حتى لا تصل إلى أجهزة الاستقبال الأخرى. يمكنك التحكّم في ترتيب تشغيل أجهزة الاستقبال ضمن عملية التطبيق نفسها. للقيام بذلك، استخدِم السمة android:priority لفلتر الغرض المطابق. يتم تشغيل أجهزة الاستقبال التي لها الأولوية نفسها بترتيب عشوائي.
  • ترسل الطريقة sendBroadcast(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 للغرض بنية اسم حزمة Java للتطبيق وتحدّد بشكل فريد حدث البث. يمكنك إرفاق معلومات إضافية بالغرض باستخدام putExtra(String, Bundle). يمكنك أيضًا حصر عملية البث في مجموعة من التطبيقات في المؤسسة نفسها من خلال استدعاء setPackage(String) على الغرض.

حظر عمليات البث باستخدام الأذونات

تسمح لك الأذونات بحصر عمليات البث في مجموعة التطبيقات التي لديها أذونات معيّنة. يمكنك فرض قيود على مُرسِل عملية البث أو جهاز الاستقبال.

إرسال عمليات البث باستخدام الأذونات

عند استدعاء sendBroadcast(Intent, String) أو sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) ، يمكنك تحديد مَعلمة إذن. لا يمكن لجهاز الاستقبال تلقّي البث إلا إذا طلب هذا الإذن باستخدام العلامة <uses-permission> في ملف البيان. إذا كان الإذن خطيرًا، يجب منحه قبل أن يتمكّن جهاز الاستقبال من تلقّي البث. على سبيل المثال، يرسل الرمز التالي عملية بث بإذن:

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>. للحصول على معلومات عن الأذونات والأمان بشكل عام، يُرجى الاطّلاع على أذونات النظام.

تلقّي عمليات البث باستخدام الأذونات

إذا حدّدت مَعلمة إذن عند تسجيل جهاز استقبال بث (إما باستخدام registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) أو في <receiver> العلامة في ملف البيان)، لا يمكن لأجهزة البث إرسال غرض إلى جهاز الاستقبال إلا إذا طلبت الإذن باستخدام العلامة <uses-permission> في ملف البيان. إذا كان الإذن خطيرًا، يجب أيضًا منح جهاز البث الإذن.

على سبيل المثال، لنفترض أنّ تطبيقك المستلِم لديه جهاز استقبال تم تعريفه في ملف البيان على النحو التالي:

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

تجنُّب عمليات البث ضمن العملية نفسها

تم تصميم عمليات البث كآلية اتصال بين العمليات (IPC) لإرسال الرسائل بين التطبيقات المختلفة أو بين النظام والتطبيقات. إنّ إرسال بث ذاتي ، وهو بث تعمل فيه جميع أجهزة الاستقبال في العملية نفسها التي أرسلت البث، غير فعّال للغاية ويؤدي إلى زيادة غير ضرورية في حمل النظام، وننصحك بشدة بتجنُّبه.

في ما يلي سيناريوهان شائعان ترسل فيهما التطبيقات عمليات بث إلى نفسها:

  • التواصل بين المكوّنات في العملية نفسها: على سبيل المثال، تمرير الأحداث أو البيانات بين الأنشطة أو الأجزاء أو الخدمات أو سلاسل التعليمات في الخلفية. بدلاً من إرسال عمليات البث، استخدِم آليات الاتصال القياسية داخل العملية ، مثل نمط المراقب أو مصادر البيانات التفاعلية:

    • مسارات Kotlin (SharedFlow وStateFlow): حلّ حديث، ومناسب في Kotlin لإصدار ومراقبة مصادر بيانات الأحداث أو تعديلات الحالة بين إجراءات فرعية ومكوّنات في تطبيقك.
    • ViewModel مشترَكة: تسهّل مشاركة البيانات والأحداث بين مكوّنات واجهة المستخدم المختلفة (مثل الأجزاء أو العناصر المركّبة) ضمن النشاط نفسه.
    • عمليات معاودة الاتصال والمستمعون: عمليات معاودة الاتصال القياسية للواجهة أو مراجع الدوال التي يتم تمريرها مباشرةً بين المكوّنات أو تسجيلها في مستودع أو وحدة تحكّم مركزية.
  • التعامل مع أحداث النظام أو المهام أو المنبّهات: على سبيل المثال، تلقّي مهمة أو منبّه أو معاودة اتصال من النظام مجدولة، ثم إرسال عملية بث لتشغيل العمل الفعلي. بدلاً من إرسال عملية بث، أكمل العمل مباشرةً ضمن هذه المهمة (مثل JobService أو WorkManager عامل) أو معالج المنبّه أو مكوّن معاودة الاتصال من النظام، أو فوِّض مباشرةً إلى فئات منطق النشاط في تطبيقك.

على الأجهزة التي تعمل بنظام Android 17 QPR2 أو إصدار أعلى، يسلّم النظام عمليات البث الذاتية بشكل أكثر كفاءة من خلال إرجاعها إلى عملية الإرسال، التي تسلّمها إلى أجهزة الاستقبال الخاصة بها على سلسلة التعليمات الرئيسية. لا يؤدي إرسال عملية بث ذاتية إلى زيادة أهمية العملية ولا يمنع تجميد عملية مخزّنة مؤقتًا، لذا لا تعتمد على عمليات البث الذاتية لإبقاء تطبيقك قيد التشغيل أو لإجراء العمل في الخلفية. بدلاً من ذلك، اختَر واجهة برمجة التطبيقات المناسبة لمهمتك كما هو موضّح في نظرة عامة على المهام في الخلفية. حتى مع هذا التحسين، تكون عمليات البث الذاتية أقل كفاءة من آليات الاتصال داخل العملية، لذا استخدِم هذه البدائل بدلاً من ذلك.

لمزيد من المعلومات عن تصميم الاتصال بين مكوّنات التطبيق، يُرجى الاطّلاع على دليل بنية التطبيق.

الاعتبارات الأمنية

في ما يلي بعض الاعتبارات الأمنية لإرسال عمليات البث وتلقّيها:

  • إذا سجّلت العديد من التطبيقات لتلقّي عملية البث نفسها في ملف البيان، يمكن أن يؤدي ذلك إلى تشغيل النظام للكثير من التطبيقات، ما يؤثر بشكل كبير في أداء الجهاز وتجربة المستخدم. لتجنُّب ذلك، ننصحك باستخدام التسجيل في السياق بدلاً من التعريف في ملف البيان. في بعض الأحيان، يفرض نظام Android نفسه استخدام أجهزة الاستقبال المسجَّلة في السياق. على سبيل المثال، لا يتم تسليم عملية البث CONNECTIVITY_ACTION إلا إلى أجهزة الاستقبال المسجَّلة في السياق.

  • لا تبث معلومات حساسة باستخدام غرض ضمني. يمكن لأي تطبيق قراءة المعلومات إذا سجّل لتلقّي البث. هناك ثلاث طرق للتحكّم في مَن يمكنه تلقّي عمليات البث:

    • يمكنك تحديد إذن عند إرسال عملية بث.
    • في Android 4.0 (مستوى واجهة برمجة التطبيقات 14) والإصدارات الأحدث، يمكنك تحديد حزمة باستخدام setPackage(String) عند إرسال رسالة بث. يحصر النظام عملية البث في مجموعة التطبيقات التي تطابق الحزمة.
  • عند تسجيل جهاز استقبال، يمكن لأي تطبيق إرسال عمليات بث قد تكون ضارة إلى جهاز استقبال تطبيقك. هناك عدة طرق للحدّ من عمليات البث التي يتلقّاها تطبيقك:

    • يمكنك تحديد إذن عند تسجيل جهاز استقبال بث.
    • بالنسبة إلى أجهزة الاستقبال التي تم تعريفها في ملف البيان، يمكنك ضبط السمة android:exported على "false" في ملف البيان. لا يتلقّى جهاز الاستقبال عمليات البث من مصادر خارج التطبيق.
  • إنّ مساحة الاسم لإجراءات البث هي مساحة اسم عالمية. تأكّد من كتابة أسماء الإجراءات والسلاسل الأخرى في مساحة اسم تملكها. وإلا، قد تتعارض عن غير قصد مع تطبيقات أخرى.

  • بما أنّ طريقة onReceive(Context, Intent) لجهاز الاستقبال تعمل على سلسلة التعليمات الرئيسية، يجب أن يتم تنفيذها وعرضها بسرعة. إذا كنت بحاجة إلى تنفيذ عمل طويل الأمد، توخَّ الحذر بشأن إنشاء سلاسل تعليمات أو بدء خدمات تُشغَّل في الخلفية لأنّ النظام يمكنه إيقاف العملية بأكملها بعد أن يعرض onReceive(). لمزيد من المعلومات، يُرجى الاطّلاع على التأثيرات على حالة العملية لتنفيذ عمل طويل الأمد، ننصحك بما يلي:

    • استدعاء goAsync() في طريقة onReceive() لجهاز الاستقبال وتمرير BroadcastReceiver.PendingResult إلى سلسلة تعليمات في الخلفية. يُبقي ذلك البث نشطًا بعد العودة من onReceive(). ومع ذلك، حتى مع هذا النهج، يتوقّع النظام أن تنتهي من البث بسرعة كبيرة (في أقل من 10 ثوانٍ). يسمح لك ذلك بنقل العمل إلى سلسلة تعليمات أخرى لتجنُّب حدوث خلل في سلسلة التعليمات الرئيسية.
    • جدولة مهمة باستخدام JobScheduler. لمزيد من المعلومات، يُرجى الاطّلاع على جدولة المهام الذكية.
  • لا تبدأ الأنشطة من أجهزة استقبال البث لأنّ تجربة المستخدم تكون غير سلسة، خاصةً إذا كان هناك أكثر من جهاز استقبال. بدلاً من ذلك، ننصحك بعرض إشعار.