אפליקציות ל-Android יכולות לשלוח או לקבל הודעות שידור ממערכת Android, באפליקציות אחרות של Android, בדומה פרסום-הרשמה של העיצוב. השידורים האלה נשלחים כשמתרחש אירוע שיש בו עניין. לדוגמה, מערכת Android שולחת שידורים כשאירועי מערכת שונים קורה, למשל, כשמתבצעת אתחול של המערכת או כשהמכשיר מתחיל להיטען. קמפיינים לקידום אפליקציות יכול גם לשלוח שידורים מותאמים אישית, למשל כדי להודיע לאפליקציות אחרות על משהו שאולי יעניין אותם (למשל, נתונים חדשים מסוימים בוצעה הורדה).
המערכת מבצעת אופטימיזציה של אספקת שידורים כדי תקינות המערכת אופטימלית. לכן, זמני האספקה של השידורים מובטחת. אפליקציות שצריכות זמן אחזור קצר בתקשורת בין תהליכים מביאים בחשבון שירותים קשורים.
אפליקציות יכולות להירשם לקבלת שידורים ספציפיים. כאשר שידור נשלח, המערכת מנתבת באופן אוטומטי שידורים לאפליקציות שנרשמו לקבל שידור מהסוג הזה.
באופן כללי, ניתן להשתמש בשידורים כמערכת העברת הודעות בין אפליקציות ולא במסגרת התהליך הרגיל של המשתמש. עם זאת, צריך להיזהר לא לנצל לרעה את ההזדמנות להגיב לשידורים ולהריץ משימות ברקע עלולות לגרום לביצועי מערכת איטיים.
מידע על שידורי המערכת
המערכת שולחת שידורים באופן אוטומטי כאשר מתרחשים אירועי מערכת שונים, למשל, כשהמערכת נכנסת למצב טיסה ויוצאת ממנו. מערכת נשלחים לכל האפליקציות שנרשמו לערוץ אירוע.
הודעת השידור עצמה עטופה בתוך Intent
אובייקט שמחרוזת הפעולה שלו מזהה את האירוע שהתרחש (לדוגמה
android.intent.action.AIRPLANE_MODE
). הכוונה יכולה גם לכלול
מידע נוסף שכלול בשדה הנוסף שלו. לדוגמה, המטוס
Intent של מצב כולל תוספת בוליאנית שמציינת אם מטוס
המצב מופעל.
לקבלת מידע נוסף על קריאה של כוונות וקבלת מחרוזת הפעולה מ: כוונה, ראו כוונות וכוונת רכישה מסננים.
לרשימה מלאה של פעולות השידור של המערכת:
קובץ BROADCAST_ACTIONS.TXT
ב-Android SDK. לכל פעולת שידור יש
שדה קבוע שמשויך אליו. לדוגמה, הערך של הקבוע
ACTION_AIRPLANE_MODE_CHANGED
הוא
android.intent.action.AIRPLANE_MODE
. תיעוד לכל פעולת שידור
זמין בשדה הקבוע המשויך אליו.
שינויים בשידורי המערכת
ככל שפלטפורמת Android מתפתחת, היא משנה מדי פעם את האופן שבו המערכת משדרת להתנהג כמו שצריך. חשוב לזכור את השינויים הבאים כדי לתמוך בכל הגרסאות של Android.
Android 14
כשהאפליקציות נמצאות בקובץ שמור
המצב, העברת השידור היא
עבר אופטימיזציה לשמירה על תקינות המערכת. לדוגמה, שידורי מערכת פחות חשובים כמו
כי ACTION_SCREEN_ON
נדחתה כשהאפליקציה במצב של קובץ שמור. לאחר שהאפליקציה עוברת מהמטמון
את המצב לתהליך פעיל
במחזור החיים, המערכת מספקת
שידורים שנדחו.
שידורים חשובים שמצוינים מניפסט מסיר אפליקציות מהמטמון באופן זמני במצב למשלוח.
9 Android
החל מ-Android 9 (רמת API 28),
NETWORK_STATE_CHANGED_ACTION
השידור אינו מקבל מידע על מיקום המשתמש או באופן אישי
נתונים שמאפשרים זיהוי של משתמשים.
כמו כן, אם האפליקציה מותקנת במכשיר עם Android מגרסה 9 ואילך,
שידורי מערכת מ-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)
.
אי אפשר להצהיר על מקלט במניפסט.
קבלת שידורים
אפליקציות יכולות לקבל שידורים בשתי דרכים: דרך מקלטים שהוצהרו כמניפסטים ונמענים שרשומים לפי ההקשר.
נמענים שהוצהרו כמניפסטים
אם תהצהירו במניפסט על מקלט שידורים, המערכת תפעיל את האפליקציה (אם האפליקציה כבר לא פועלת) כשהשידור נשלח.
כדי להצהיר על מקלט שידורים במניפסט, מבצעים את השלבים הבאים:
יש לציין את
<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="APP_SPECIFIC_BROADCAST" /> </intent-filter> </receiver>
מסנני Intent מציינים את פעולות השידור שאליהן המקלט נרשם.
מחלקה משנית
BroadcastReceiver
והטמעה שלonReceive(Context, Intent)
. של מקלט השידורים ביומנים לדוגמה הבאים, ומציג את התוכן של השידור:Kotlin
private const val TAG = "MyBroadcastReceiver" class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { StringBuilder().apply { append("Action: ${intent.action}\n") append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n") toString().also { log -> Log.d(TAG, log) val binding = ActivityNameBinding.inflate(layoutInflater) val view = binding.root setContentView(view) Snackbar.make(view, log, Snackbar.LENGTH_LONG).show() } } } }
Java
public class MyBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "MyBroadcastReceiver"; @Override public void onReceive(Context context, Intent intent) { StringBuilder sb = new StringBuilder(); sb.append("Action: " + intent.getAction() + "\n"); sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n"); String log = sb.toString(); Log.d(TAG, log); ActivityNameBinding binding = ActivityNameBinding.inflate(layoutInflater); val view = binding.root; setContentView(view); Snackbar.make(view, log, Snackbar.LENGTH_LONG).show(); } }
כדי להפעיל קישור תצוגות מפורטות: הגדרת viewBinding ברמת המודול build.gradle.
מנהל חבילות המערכת רושם את הנמען כשהאפליקציה מותקנת. לאחר מכן, המקבל הופך לנקודת כניסה נפרדת לאפליקציה. המשמעות היא שהמערכת יכולה להפעיל את האפליקציה ולהעביר את השידור אם היא לא פועלת כרגע.
המערכת יוצרת רכיב BroadcastReceiver
חדש
כדי לטפל בכל שידור שהוא מקבל. האובייקט הזה תקף בלבד
במהלך השיחה אל onReceive(Context, Intent)
. לאחר שהקוד
מוחזרת מהשיטה הזאת, המערכת מתייחסת לרכיב שכבר לא
פעיל.
נמענים שרשומים לפי הקשר
מקלטים שרשומים לפי הקשר מקבלים שידורים כל עוד הם נרשמים
הקשר חוקי. לדוגמה, אם נרשמת במסגרת
Activity
ההקשר, אתם מקבלים שידורים כל עוד הפעילות לא מושמדת. אם
להירשם בהקשר של האפליקציה, אתם מקבלים שידורים כל עוד האפליקציה
פועלת.
כדי לרשום את המקבל עם הקשר, מבצעים את השלבים הבאים:
בקובץ ה-build ברמת המודול של האפליקציה, יש לכלול את גרסה 1.9.0 ואילך של ספריית AndroidX Core:
מגניב
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") }
יוצרים מופע של
BroadcastReceiver
:Kotlin
val br: BroadcastReceiver = MyBroadcastReceiver()
Java
BroadcastReceiver br = new MyBroadcastReceiver();
יוצרים מופע של
IntentFilter
:Kotlin
val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
Java
IntentFilter filter = new IntentFilter(APP_SPECIFIC_BROADCAST);
בחירה אם לייצא את מקלט השידורים ולראות אותו מאפליקציות אחרות במכשיר. אם המקלט הזה מאזין לשידורים שנשלחו מהמערכת או מאפליקציות אחרות — אפילו מאפליקציות אחרות שבבעלותך — משתמשות סימון
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; if (listenToBroadcastsFromOtherApps) { receiverFlags = ContextCompat.RECEIVER_EXPORTED; } else { receiverFlags = ContextCompat.RECEIVER_NOT_EXPORTED; }
כדי לרשום את המקבל צריך להתקשר
registerReceiver()
:Kotlin
ContextCompat.registerReceiver(context, br, filter, receiverFlags)
Java
ContextCompat.registerReceiver(context, br, filter, receiverFlags);
כדי להפסיק לקבל שידורים, צריך להתקשר למספר
unregisterReceiver(android.content.BroadcastReceiver)
. חשוב לבטל את הרישום של המקבל כאשר אין לך יותר צורך בו, או אינו חוקי עוד.שימו לב איפה נרשמים ומבטלים את הרישום של המקבל, לדוגמה, אם תרשום מקבל ב
onCreate(Bundle)
לפי ההקשר של הפעילות, עליך צריך לבטל את הרישום ב-onDestroy()
כדי למנוע הדלפה של המקבל מחוץ להקשר של הפעילות. אם תירשמו מקלט בonResume()
, צריך לבטל את הרישום ב-onPause()
כדי לרשום אותו מספר פעמים (אם אינך רוצה לקבל שידורים כאשר תשהו אותם, וכך תפחיתו תקורה מיותרת של המערכת). לא מומלץ ביטול הרישום ב-onSaveInstanceState(Bundle)
, כי לא מתבצעת קריאה לכך אם המשתמש חוזר בערימת ההיסטוריה.
השפעות על מצב התהליך
האם BroadcastReceiver
פועל או לא משפיע על התהליך שהוא מכיל, דבר שעלול לשנות את
הסבירות לקיבוע על ידי המערכת. תהליך שפועל בחזית מפעיל את ה-method onReceive()
של המקבל.
מפעיל את התהליך מלבד במצב של לחץ זיכרון קיצוני.
ה- BroadcastReceiver מושבת אחרי onReceive()
. המארח של המקבל
חשוב רק כמו רכיבי האפליקציה שלו. אם התהליך הזה מארחים רק
מקבל הצהרה במניפסט (מצב הזה קורה לעיתים קרובות לאפליקציות שהמשתמש מעולם לא
או שלא הייתה אינטראקציה איתו לאחרונה), המערכת עלולה להרוג אותו אחרי onReceive()
כדי
זמינים לתהליכים קריטיים יותר.
לכן, מקלטי השידורים לא אמורים להפעיל שרשורים ברקע ממושכות.
המערכת יכולה להפסיק את התהליך בכל שלב אחרי onReceive()
כדי לשחזר בעלות
הוא לסיים את השרשור שנוצר. כדי להמשיך את התהליך, כדאי לקבוע מועד
JobService
מהמקבל באמצעות JobScheduler
כדי שהמערכת תדע שהתהליך עדיין פועל.
פרטים נוספים מופיעים בקטע סקירה כללית של עבודה ברקע.
שליחת שידורים
ב-Android יש שלוש דרכים שבהן אפליקציות יכולות לשלוח שידור:
sendOrderedBroadcast(Intent, String)
שולחת שידורים למקלט אחד בכל פעם. בזמן שכל מקלט מבצע בתורו, הוא יכול להפיץ את התוצאה למקבל הבא, או לבטל לגמרי את השידור כדי שלא יועבר לערוץ אחר הנמען. אפשר לשלוט במקבלי ההזמנות שפועלים באמצעות המאפיין android:Priorityity של מסנן ה-Intent התואם; של מקלטים אותה עדיפות תפעל בסדר שרירותי.- השיטה
sendBroadcast(Intent)
שולחת שידורים לכל המקלטים לפי סדר לא מוגדר. הפעולה הזו נקראת 'רגילה' שליחת הודעה לכולם. פעולה זו יעילה יותר, אבל המשמעות היא שהנמענים לא יכולים לקרוא תוצאות ממקלטים אחרים, הפצת נתונים שהתקבלו מהשידור, או לבטל את השידור.
קטע הקוד הבא מדגים איך לשלוח שידור באמצעות יצירה של
כוונה וקריאה אל sendBroadcast(Intent)
.
Kotlin
Intent().also { intent -> intent.setAction("com.example.broadcast.MY_NOTIFICATION") intent.putExtra("data", "Nothing to see here, move along.") sendBroadcast(intent) }
Java
Intent intent = new Intent(); intent.setAction("com.example.broadcast.MY_NOTIFICATION"); intent.putExtra("data", "Nothing to see here, move along."); sendBroadcast(intent);
הודעת השידור מוקפת באובייקט Intent
.
מחרוזת הפעולה של ה-Intent חייבת לספק את התחביר של שם חבילת ה-Java של האפליקציה, וגם
לזהות באופן ייחודי את אירוע השידור. אפשר לצרף מידע נוסף
ל-Intent עם putExtra(String, Bundle)
.
אפשר גם להגביל את השידור לקבוצה של אפליקציות באותו ארגון. כדי לעשות את זה,
קריאה אל setPackage(String)
מתוך כוונה.
הגבלת שידורים עם הרשאות
הרשאות מאפשרות להגביל את השידורים לקבוצות של האפליקציות שבהן התכונה נמצאת הרשאות מסוימות. אפשר לאכוף הגבלות על השולח או על במקלט של שידור.
שליחה עם הרשאות
כשמתקשרים אל sendBroadcast(Intent, String)
או
sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
, אפשר לציין
פרמטר הרשאה. רק נמענים שביקשו את ההרשאה הזו עם
את התג
Kotlin
sendBroadcast(Intent(BluetoothDevice.ACTION_FOUND), Manifest.permission.BLUETOOTH_CONNECT)
Java
sendBroadcast(new Intent(BluetoothDevice.ACTION_FOUND), Manifest.permission.BLUETOOTH_CONNECT)
כדי לקבל את השידור, האפליקציה המקבלת צריכה לבקש את ההרשאה הזו: מוצגת למטה:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
אפשר לציין הרשאת מערכת קיימת, כמו
BLUETOOTH_CONNECT
או להגדיר הרשאה מותאמת אישית עם
רכיב <permission>
. עבור
מידע על הרשאות ואבטחה באופן כללי, ראו מערכת
הרשאות.
קבלה עם הרשאות
אם מציינים פרמטר הרשאה כשרושמים מקלט שידורים
(עם registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
או בתוך
התג <receiver>
), רק רשתות שידור שביקשו הרשאה באמצעות
תג <uses-permission>
במניפסט שלהם (ולאחר מכן קיבל את ההרשאה אם הוא
מסוכן) יכול לשלוח Intent למקבל.
לדוגמה, נניח שלאפליקציה המקבלת יש נמען שהוצהר במניפסט כ- מוצגת למטה:
<receiver android:name=".MyBroadcastReceiver"
android:permission="android.permission.BLUETOOTH_CONNECT">
<intent-filter>
<action android:name="android.intent.action.ACTION_FOUND"/>
</intent-filter>
</receiver>
לחלופין, לאפליקציה המקבלת יש מקלט רשום לפי הקשר כפי שמוצג בהמשך:
Kotlin
var filter = IntentFilter(Intent.ACTION_FOUND) registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null )
Java
IntentFilter filter = new IntentFilter(Intent.ACTION_FOUND); registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null );
לאחר מכן, כדי שתהיה אפשרות לשלוח שידורים למקבלי ההודעות האלה, האפליקציה השולחת מבקשים את ההרשאה באופן הבא:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
שיקולי אבטחה ושיטות מומלצות
הנה כמה שיקולי אבטחה ושיטות מומלצות לשליחה קבלת שידורים:
אם אפליקציות רבות נרשמו לקבלת אותו שידור הוא עלול לגרום למערכת להפעיל אפליקציות רבות, דבר שעלול לגרום השפעה משמעותית על ביצועי המכשיר ועל חוויית המשתמש. כדי להימנע אני רוצה להשתמש ברישום הקשר במקום בהצהרת מניפסט. לפעמים, מערכת Android עצמה אוכפת שימוש ברישומים לפי הקשר הנמען. לדוגמה, השידור
CONNECTIVITY_ACTION
מועבר רק למקבלים עם רישום לפי ההקשר.אסור לשדר מידע רגיש מתוך כוונה מרומזת. כל אפליקציה שנרשם לקבלת השידור יכולה לקרוא את המידע הזה. יש שלוש דרכים לקבוע מי יכול לקבל את השידורים שלכם:
- אפשר להגדיר הרשאה כששולחים שידור.
- ב-Android מגרסה 4.0 ואילך, ניתן לציין
package עם
setPackage(String)
בעת שליחת שידור. המערכת מגבילה את השידור לקבוצת האפליקציות מתאימים את החבילה.
כשרושמים מקלט, כל אפליקציה יכולה לשלוח תוכן שעלול להיות זדוני שידורים למקלט של האפליקציה. יש כמה דרכים להגביל שידורים שהאפליקציה שלך מקבלת:
- ניתן לציין הרשאה במהלך רישום של מקלט שידורים.
- לנמענים עם הצהרה במניפסט, אפשר להגדיר את האפשרות android:exported שיוך ל-"false" במניפסט. הנמען לא מקבל שידורים ממקורות מחוץ לאפליקציה.
מרחב השמות לפעולות שידור הוא גלובלי. צריך לוודא ששמות הפעולות ומחרוזות אחרות כתובות במרחב שמות שנמצא בבעלותך, אחרת ייתכן מתנגשות בטעות עם אפליקציות אחרות.
כי שיטת
onReceive(Context, Intent)
של המקבל פועלת על ב-thread הראשי, הוא אמור לפעול ולחזור במהירות. אם צריך תבצעו פעולות ממושכות, שימו לב להרצת שרשורים או להתחיל בשירותים ברקע מכיוון שהמערכת יכולה להפסיק את התהליך כולו לאחר אפשרות החזרה במחירonReceive()
. למידע נוסף, ראו ההשפעה על התהליך כדי לבצע עבודות לטווח ארוך, אנחנו מומלץ:- נתקשר למספר
goAsync()
בעודonReceive()
של המקבל ומעביר את הערךBroadcastReceiver.PendingResult
לשרשור ברקע. זה ישאיר את השידור פעיל אחרי החזרה מ-onReceive()
. עם זאת, גם בגישה זו, המערכת מצפה ממך לסיים את השידור מהר מאוד (פחות מ-10 שניות). היא מאפשרת מתאימים לשרשור אחר כדי למנוע תקלות ב-thread הראשי. - תזמון עבודה עם
JobScheduler
. לקבלת מידע נוסף מידע נוסף ראו Intelligent Job תזמון.
- נתקשר למספר
אין להתחיל פעילויות ממקלטי שידורים כי חוויית המשתמש צורם; במיוחד אם יש יותר ממקלט אחד. במקום זאת, כדאי התראה.