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

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

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

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

מידע על שידורי מערכת

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

האובייקט Intent עוטף את ההודעה לשידור. מחרוזת action מזהה את האירוע שהתרחש, למשל android.intent.action.AIRPLANE_MODE. יכול להיות שהכוונה תכלול גם מידע נוסף שמאוגד בשדה הנוסף שלה. לדוגמה, כוונת הפעולה Airplane Mode (מצב טיסה) כוללת תוסף בוליאני שמציין אם מצב טיסה מופעל או לא.

מידע נוסף על קריאת כוונות וקבלת מחרוזת הפעולה מתוך כוונה זמין במאמר Intents and 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 (רמת API‏ 28), השידור NETWORK_STATE_CHANGED_ACTION לא מקבל מידע על מיקום המשתמש או נתונים אישיים מזהים.

אם האפליקציה מותקנת במכשיר עם Android 9.0 (רמת API‏ 28) או גרסה מתקדמת יותר, המערכת לא כוללת שידורי Wi-Fi של מזהי SSID, מזהי BSSID, פרטי חיבור או תוצאות סריקה. כדי לקבל את המידע הזה, אפשר להתקשר למספר 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, תקבלו שידורים כל עוד הפעילות נשארת פעילה. אם נרשמים באמצעות הקשר של האפליקציה, מקבלים שידורים כל עוד האפליקציה פועלת.

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

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

    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. בוחרים אם לייצא את מקלט השידור כך שיהיה גלוי לאפליקציות אחרות במכשיר. אם הרכיב הזה להעברת נתונים מאזין לשידורים שנשלחים מהמערכת או מאפליקציות אחרות – אפילו מאפליקציות אחרות שבבעלותכם – צריך להשתמש בדגל 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 methods של מחזור החיים: מקלט השידור מקבל עדכונים רק כשהאפליקציה במצב resumed.
  • LifecycleStartEffect או פעילות onStart/onStop methods של מחזור החיים: מקלט השידור מקבל עדכונים רק כשהאפליקציה במצב resumed.
  • DisposableEffect: מקלט השידורים מקבל עדכונים רק כשהרכיב הניתן להרכבה נמצא בעץ ההרכבה. ההיקף הזה לא מצורף להיקף מחזור החיים של הפעילות. כדאי לרשום את המקלט בהקשר של האפליקציה. הסיבה לכך היא שהרכיב הקומפוזבילי יכול באופן תיאורטי לשרוד את היקף מחזור החיים של הפעילות ולגרום לדליפת הפעילות.
  • פעילות onCreate/onDestroy: מקלט השידור מקבל עדכונים בזמן שהפעילות במצב שנוצר. חשוב לבטל את ההרשמה ב-onDestroy() ולא ב-onSaveInstanceState(Bundle), כי יכול להיות שהפעולה הזו לא תתבצע.
  • היקף מותאם אישית: לדוגמה, אפשר לרשום מקלט בהיקף ViewModelכדי שהוא ימשיך לפעול גם אחרי יצירה מחדש של הפעילות. חשוב להשתמש בהקשר של האפליקציה כדי לרשום את המקלט, כי המקלט יכול להיות פעיל מעבר להיקף מחזור החיים של הפעילות ולגרום לדליפת הפעילות.

יצירת קומפוזיציות עם שמירת מצב וקומפוזיציות ללא שמירת מצב

ל-Compose יש פונקציות שניתנות להגדרה עם שמירת מצב ופונקציות שניתנות להגדרה בלי שמירת מצב. רישום או ביטול רישום של מקלט שידורים בתוך פונקציה שאפשר להרכיב הופך אותו למקלט עם מצב. הקומפוזבילי הוא לא פונקציה דטרמיניסטית שמציגה את אותו התוכן כשמעבירים לה את אותם הפרמטרים. המצב הפנימי יכול להשתנות בהתאם לקריאות ל-broadcast receiver הרשום.

כשיטה מומלצת ב-Compose, מומלץ לפצל את פונקציות ה-Composable לגרסאות עם שמירת מצב ולגרסאות בלי שמירת מצב. לכן מומלץ להעביר את יצירת מקלט השידורים מחוץ ל-Composable כדי להפוך אותו ל-stateless:

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

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

המערכת יוצרת אובייקט רכיב חדש של BroadcastReceiver כדי לטפל בכל שידור שהיא מקבלת. האובייקט הזה תקף רק למשך השיחה עם onReceive(Context, Intent). אחרי שהקוד חוזר מהשיטה הזו, המערכת מחשיבה את הרכיב כלא פעיל.

השפעות על מצב התהליך

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

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

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

שליחת הודעות משפחתיות

ב-Android יש שתי דרכים שבהן אפליקציות יכולות לשלוח שידורים:

  • בשיטה sendOrderedBroadcast(Intent, String), השידורים נשלחים למקלט אחד בכל פעם. כשכל מקלט מופעל בתורו, הוא יכול להעביר תוצאה למקלט הבא. הוא יכול גם לבטל לחלוטין את השידור כך שהוא לא יגיע למקלטים אחרים. אתם יכולים לקבוע את הסדר שבו רכיבי Receiver פועלים באותו תהליך של אפליקציה. כדי לעשות את זה, משתמשים במאפיין android:priority של מסנן ה-Intent התואם. ההרצה של מקלטים עם אותה עדיפות מתבצעת בסדר שרירותי.
  • ה-method‏ 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) ב-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 למקלט. אם ההרשאה מסוכנת, צריך להעניק אותה גם לשידור.

לדוגמה, נניח שאפליקציית היעד מכילה רכיב 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=".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 מועבר רק למקבלי שידור שרשומים בהקשר.

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

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

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

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

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