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

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

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

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

لمحة عن الرسائل المُرسَلة من النظام

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

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

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

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

للحصول على قائمة كاملة بإجراءات البث على مستوى النظام، اطّلِع على BROADCAST_ACTIONS.TXTملف في حزمة تطوير البرامج (SDK) لنظام التشغيل Android. يحتوي كل إجراء بث على حقل ثابت مرتبط به. على سبيل المثال، قيمة الثابت ACTION_AIRPLANE_MODE_CHANGED هي android.intent.action.AIRPLANE_MODE. تتوفّر مستندات كل إجراء بث في الحقل المتغيّر المرتبط به.

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

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

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

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

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

Android 7.0

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

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

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

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

المستلمون المسجَّلون بالسياق

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

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

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

    رائع

    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. أنشئ مثيلًا من 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). احرص على إلغاء تسجيل جهاز الاستقبال عندما لا يعود مطلوبًا أو عندما لم يعُد السياق صالحًا.

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

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

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>
    

    تحدِّد فلاتر الأهداف إجراءات البث التي يشترك فيها جهاز الاستقبال.

  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) عمليات البث إلى جميع المستلِمين بترتيب غير محدّد. يُعرف هذا باسم "البث العادي". وهذا الإجراء أكثر فعالية، ولكنه يعني أنّه لا يمكن للمستلِمين قراءة النتائج من المستلِمين الآخرين أو نشر البيانات التي تمّ تلقّيها من البث أو إيقاف البث.

يوضّح مقتطف الرمز البرمجي التالي كيفية إرسال بث من خلال إنشاء رمز 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 للتطبيق وتحديدًا فريدة لحدث البث. يمكنك إرفاق معلومات إضافية بالهدف باستخدام 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" />

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

في ما يلي بعض النقاط التي يجب أخذها في الاعتبار بشأن الأمان عند إرسال البثّات واستلامها:

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

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

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

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

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

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