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

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

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

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

لمحة عن عمليات بث النظام

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

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

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

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

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

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

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

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

  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 للغرض بنية اسم حزمة 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> في البيان إرسال `Intent` إلى جهاز الاستقبال. إذا كان الإذن خطيرًا، يجب أيضًا منح المُرسِل الإذن.

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

<!-- 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 إلا إلى أجهزة الاستقبال المسجَّلة في السياق.

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

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

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

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

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