סקירה כללית של שידורים

אפליקציות ל-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 14

כשאפליקציות נמצאות במצב שמאוחסן במטמון, המערכת מבצעת אופטימיזציה של העברת השידור כדי לשמור על תקינות המערכת. לדוגמה, המערכת דוחה שידורי מערכת פחות חשובים כמו ACTION_SCREEN_ON בזמן שהאפליקציה נמצאת במצב שמאוחסן במטמון. אחרי שהאפליקציה עוברת ממצב השמירה במטמון למחזור חיים של תהליך פעיל, המערכת שולחת את כל השידורים שנדחו.

שידורים חשובים שמוצהרים במניפסט מסירים באופן זמני אפליקציות מהמצב ששמור במטמון לצורך העברה.

Android 9

החל מגרסה 9 של Android‏ (רמה 28 של API), ההודעה NETWORK_STATE_CHANGED_ACTION לא מקבלת מידע על המיקום של המשתמש או על פרטים אישיים מזהים.

אם האפליקציה מותקנת במכשיר עם Android 9.0 (רמת API 28) ואילך, המערכת לא כוללת את מזהי ה-SSID, מזהי ה-BSSID, פרטי החיבור או תוצאות הסריקה בשידורי Wi-Fi. כדי לקבל את המידע הזה, צריך להתקשר למספר getConnectionInfo() במקום זאת.

Android 8.0

החל מגרסה Android 8.0‏ (רמת API‏ 26), המערכת מטילה הגבלות נוספות על מקלטי הודעות שהוגדרו במניפסט.

אם האפליקציה שלכם מטרגטת ל-Android מגרסה 8.0 ואילך, אי אפשר להשתמש במניפסט כדי להצהיר על מקלט לרוב השידורים המשתמעים (שידורים שלא מטרגטים את האפליקציה שלכם באופן ספציפי). עדיין אפשר להשתמש במקלט שמירשם לפי הקשר כשהמשתמש משתמש באפליקציה באופן פעיל.

Android 7.0

בגרסה Android 7.0 (רמת API ‏24) ואילך, לא נשלחים שידורי המערכת הבאים:

בנוסף, אפליקציות שמטרגטות את Android מגרסה 7.0 ואילך צריכות לרשום את השידור של CONNECTIVITY_ACTION באמצעות registerReceiver(BroadcastReceiver, IntentFilter). הצהרה על מקלט במניפסט לא עובדת.

קבלת שידורים

אפליקציות יכולות לקבל שידורים בשתי דרכים: באמצעות מקלטים שמתועדים בהקשר ומקלטים שמוצהרים במניפסט.

נמענים שנרשמו בהקשר

מקלטים שמסתמכים על הרשמה לפי הקשר מקבלים שידורים כל עוד ההקשר שלהם בתוקף. בדרך כלל, הזמן הזה הוא בין הקריאות ל-registerReceiver ל-unregisterReceiver. ההקשר של הרישום הופך ללא תקף גם כשהמערכת משמידה את ההקשר התואם. לדוגמה, אם נרשמים בהקשר של Activity, מקבלים שידורים כל עוד הפעילות פעילה. אם תירשמו באמצעות ה-Application context, תקבלו שידורים כל עוד האפליקציה פועלת.

כדי לרשום מקלט עם הקשר, מבצעים את השלבים הבאים:

  1. בקובץ ה-build ברמת המודול של האפליקציה, צריך לכלול את ספריית הליבה של AndroidX בגרסה 1.9.0 ואילך:

    Groovy

    dependencies {
        def core_version = "1.13.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.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.13.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.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). חשוב לבטל את הרישום של המקלט כשלא צריכים אותו יותר או שההקשר כבר לא תקף.

ביטול הרישום של מקלט השידור

בזמן שהמקלט של השידור רשום, הוא מכיל הפניה ל-Context שבו רשמתם אותו. מצב כזה עלול לגרום לדליפות אם ההיקף הרשום של הנמען חורג מהיקף מחזור החיים של ההקשר. לדוגמה, זה יכול לקרות כשרושמים מקלט ברמת הפעילות, אבל שוכחים לבטל את הרישום שלו כשהמערכת משמידה את הפעילות. לכן, תמיד צריך לבטל את הרישום של מקלט השידור.

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 מציינים את פעולות השידור שהמקלט שלכם נרשם אליהן.

  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 של מסנן ה-Intent התואם. מקלטים באותה עדיפות מופעלים בסדר שרירותי.
  • השיטה 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) ב-intent.

הגבלת שידורים באמצעות הרשאות

ההרשאות מאפשרות להגביל את השידור לקבוצת האפליקציות שיש להן הרשאות מסוימות. אתם יכולים לאכוף הגבלות על השולח או על הנמען של השידור.

שליחת שידורים עם הרשאות

כשקוראים ל-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" />

שיקולי אבטחה

ריכזנו כאן כמה הנחיות אבטחה לשליחה ולקבלה של שידורים:

  • אם הרבה אפליקציות נרשמו לקבלת אותה שידור ב-manifest שלהן, המערכת עשויה להפעיל הרבה אפליקציות, וכתוצאה מכך תהיה השפעה משמעותית על ביצועי המכשיר ועל חוויית המשתמש. כדי למנוע זאת, מומלץ להשתמש ברישום הקשר במקום בהצהרה על מניפסט. לפעמים, מערכת Android עצמה אוכפת את השימוש במקלטים שנרשמו לפי הקשר. לדוגמה, השידור CONNECTIVITY_ACTION מועבר רק לנמענים שרשומים לפי הקשר.

  • אין לשדר מידע רגיש באמצעות כוונה משתמעת. כל אפליקציה יכולה לקרוא את המידע אם היא נרשמת לקבלת השידור. יש שלוש דרכים לקבוע מי יוכל לצפות בשידורים שלכם:

    • אתם יכולים לציין הרשאה כשאתם שולחים שידור חי.
    • ב-Android 4.0 (רמת API 14) ואילך, אפשר לציין חבילת setPackage(String) כששולחים שידור. המערכת מגבילה את השידור לקבוצת האפליקציות שתואמות לחבילה.
  • כשמגדירים מקלט, כל אפליקציה יכולה לשלוח שידור שעלול להיות זדוני למקלט של האפליקציה. יש כמה דרכים להגביל את השידור שהאפליקציה מקבלת:

    • אפשר לציין הרשאה כשרושמים מקלט שידור.
    • במקרה של מקלטים שמוגדרים במניפסט, אפשר להגדיר את המאפיין android:exported לערך 'false' במניפסט. המכשיר לא מקבל שידורים ממקורות מחוץ לאפליקציה.
  • מרחב השמות של פעולות השידור הוא גלובלי. חשוב לוודא ששמות הפעולות ומחרוזות אחרות נכתבים במרחב שמות שבבעלותכם. אחרת, יכול להיות שתיווצר לכם התנגשות לא מכוונת עם אפליקציות אחרות.

  • מכיוון ש-method‏ onReceive(Context, Intent) של מקלט פועל בשרשור הראשי, הוא אמור לפעול ולחזור במהירות. אם אתם צריכים לבצע עבודה ממושכת, חשוב להיזהר ביצירת חוטים או בהפעלת שירותי רקע, כי המערכת יכולה להרוג את התהליך כולו אחרי שה-onReceive() מוחזר. למידע נוסף, ראו השפעה על מצב התהליך. כדי לבצע משימות ממושכות, מומלץ:

    • קריאה ל-goAsync() בשיטה onReceive() של המכשיר המקבל והעברת BroadcastReceiver.PendingResult לשרשור ברקע. כך השידור יישאר פעיל אחרי החזרה מ-onReceive(). עם זאת, גם בגישה הזו, המערכת מצפה שתסיימו את השידור במהירות רבה (תוך פחות מ-10 שניות). עם זאת, הוא מאפשר להעביר עבודה ל-thread אחר כדי למנוע תקלה ב-thread הראשי.
    • תזמון של משימה באמצעות JobScheduler. מידע נוסף זמין במאמר תזמון משימות חכם.
  • אל תתחילו פעילויות ממקלטי שידור, כי חוויית המשתמש תהיה מטרידה, במיוחד אם יש יותר ממקלט אחד. במקום זאת, מומלץ להציג הודעה.