מסנני Intent ומסנני Intent

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

  • התחלת פעילות

    Activity מייצג מסך בודד באפליקציה. אפשר להתחיל מופע חדש של Activity על ידי העברת Intent אל startActivity(). ה-Intent מתאר את הפעילות שצריך להתחיל ומכיל את כל הנתונים הנדרשים.

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

  • הפעלת שירות

    Service הוא רכיב שמבצע פעולות ברקע בלי ממשק משתמש. ב-Android מגרסה 5.0 (רמת API‏ 21) ואילך, אפשר להפעיל שירות באמצעות JobScheduler. מידע נוסף על JobScheduler זמין בAPI-reference documentation.

    בגרסאות שקודמות ל-Android 5.0 (רמת API‏ 21), אפשר להפעיל שירות באמצעות שיטות של המחלקה Service. כדי להפעיל שירות שמבצע פעולה חד-פעמית (כמו הורדת קובץ), אפשר להעביר Intent אל startService(). ה-Intent מתאר את השירות שצריך להפעיל ומכיל את כל הנתונים הדרושים.

    אם השירות מתוכנן עם ממשק לקוח-שרת, אפשר לקשר לשירות מרכיב אחר על ידי העברת Intent אל bindService(). מידע נוסף זמין במדריך בנושא שירותים.

  • העברת שידור

    שידור הוא הודעה שכל אפליקציה יכולה לקבל. המערכת מעבירה שידורים שונים לאירועי מערכת, למשל כשהמערכת מופעלת או כשהמכשיר מתחיל להיטען. אפשר להעביר שידור לאפליקציות אחרות על ידי העברת Intent אל sendBroadcast() או אל sendOrderedBroadcast().

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

סוגי כוונות

יש שני סוגים של כוונות:

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

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

איור 1. איך אובייקט Intent מרומז מועבר דרך המערכת כדי להתחיל Activity אחרת: [1] Activity א' יוצרת Intent עם תיאור פעולה ומעבירה אותו אל startActivity(). ‫[2] מערכת Android מחפשת בכל האפליקציות מסנן Intent שתואם ל-Intent. כשהמערכת מוצאת התאמה, [3] היא מפעילה את ה-Activity התואם (Activity ב') על ידי הפעלת ה-method‏ onCreate() והעברת ה-Intent אליה.

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

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

זהירות: כדי לוודא שהאפליקציה מאובטחת, תמיד צריך להשתמש ב-Intent מפורש כשמפעילים Service, ולא להצהיר על מסנני Intent לשירותים. שימוש ב-intent מרומז כדי להפעיל שירות הוא סיכון אבטחה, כי אי אפשר לדעת איזה שירות יגיב ל-intent, והמשתמש לא יכול לראות איזה שירות מופעל. החל מ-Android 5.0 (רמת API‏ 21), המערכת זורקת חריגה אם קוראים ל-bindService() עם Intent משתמע.

יצירת Intent

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

המידע העיקרי שכלול ב-Intent הוא:

שם הרכיב
שם הרכיב שצריך להפעיל.

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

הערה: כשמפעילים Service, תמיד צריך לציין את שם הרכיב. אחרת, לא תוכלו לדעת איזה שירות יגיב ל-Intent, והמשתמש לא יוכל לראות איזה שירות מתחיל.

השדה הזה של Intent הוא אובייקט ComponentName, שאפשר לציין באמצעות שם מחלקה מלא של רכיב היעד, כולל שם החבילה של האפליקציה, לדוגמה, com.example.ExampleActivity. אפשר להגדיר את שם הרכיב באמצעות setComponent(), setClass(), setClassName() או באמצעות ה-constructor של Intent.

פעולה
מחרוזת שמציינת את הפעולה הכללית שצריך לבצע (למשל view או pick).

במקרה של Intent של שידור, זו הפעולה שהתרחשה ומדווחת. הפעולה קובעת במידה רבה את המבנה של שאר ה-Intent, ובמיוחד את המידע שכלול בנתונים ובתוספות.

אתם יכולים לציין פעולות משלכם לשימוש על ידי כוונות בתוך האפליקציה (או לשימוש על ידי אפליקציות אחרות כדי להפעיל רכיבים באפליקציה שלכם), אבל בדרך כלל אתם מציינים קבועי פעולה שמוגדרים על ידי המחלקה Intent או מחלקות אחרות של מסגרת. הנה כמה פעולות נפוצות להתחלת פעילות:

ACTION_VIEW
משתמשים בפעולה הזו ב-Intent עם startActivity() כשיש מידע שאפשר להציג למשתמש בפעילות, כמו תמונה שאפשר להציג באפליקציית גלריה או כתובת שאפשר להציג באפליקציית מפות.
ACTION_SEND
הפעולה הזו נקראת גם Intent לשיתוף. צריך להשתמש בה ב-Intent עם startActivity() כשיש נתונים שהמשתמש יכול לשתף באמצעות אפליקציה אחרת, כמו אפליקציית אימייל או אפליקציה לשיתוף במדיה חברתית.

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

אפשר לציין את הפעולה של כוונה באמצעות setAction() או באמצעות פונקציית ה-constructor‏ Intent.

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

Kotlin

const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"

Java

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
נתונים
ה-URI (אובייקט Uri) שמפנה לנתונים שצריך לבצע עליהם פעולה ו/או לסוג ה-MIME של הנתונים האלה. סוג הנתונים שסופקו בדרך כלל מוכתב על ידי הפעולה של ה-Intent. לדוגמה, אם הפעולה היא ACTION_EDIT, הנתונים צריכים להכיל את ה-URI של המסמך שרוצים לערוך.

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

כדי להגדיר רק את ה-URI של הנתונים, צריך להפעיל את setData(). כדי להגדיר רק את סוג ה-MIME, צריך להפעיל את setType(). במקרה הצורך, אפשר להגדיר את שניהם באופן מפורש באמצעות setDataAndType().

זהירות: אם רוצים להגדיר גם את ה-URI וגם את סוג ה-MIME, אל תפעילו את setData() ו-setType() כי כל אחד מהם מבטל את הערך של השני. תמיד צריך להשתמש ב-setDataAndType() כדי להגדיר גם את ה-URI וגם את סוג ה-MIME.

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

הרשימה המלאה של הקטגוריות מופיעה בתיאור הסוג Intent.

אפשר לציין קטגוריה באמצעות addCategory().

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

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

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

לדוגמה, כשיוצרים Intent לשליחת אימייל באמצעות ACTION_SEND, אפשר לציין את הנמען to באמצעות המפתח EXTRA_EMAIL, ולציין את הנושא subject באמצעות המפתח EXTRA_SUBJECT.

המחלקה Intent מציינת הרבה קבועים של EXTRA_* לסוגי נתונים סטנדרטיים. אם אתם צריכים להצהיר על מפתחות נוספים משלכם (ל-intent שהאפליקציה מקבלת), הקפידו לכלול את שם החבילה של האפליקציה כתחילית, כמו בדוגמה הבאה:

Kotlin

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

זהירות: אל תשתמשו בנתונים של Parcelable או Serializable כשאתם שולחים Intent שאתם מצפים שאפליקציה אחרת תקבל. אם אפליקציה מנסה לגשת לנתונים באובייקט Bundle אבל אין לה גישה למחלקה שעברה המרה ל-Parcelable או ל-Serializable, המערכת מקפיצה RuntimeException.

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

מידע נוסף זמין במאמר על השיטה setFlags().

דוגמה ל-Intent מפורש

Intent מפורש הוא Intent שמשמש להפעלת רכיב ספציפי של אפליקציה, כמו פעילות או שירות מסוימים באפליקציה. כדי ליצור Intent מפורש, מגדירים את שם הרכיב לאובייקט Intent – כל שאר המאפיינים של ה-Intent הם אופציונליים.

לדוגמה, אם יצרתם באפליקציה שירות בשם DownloadService, שנועד להוריד קובץ מהאינטרנט, אתם יכולים להפעיל אותו באמצעות הקוד הבא:

Kotlin

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
val downloadIntent = Intent(this, DownloadService::class.java).apply {
    data = Uri.parse(fileUrl)
}
startService(downloadIntent)

Java

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);

ה-constructor של Intent(Context, Class) מספק לאפליקציה Context ולרכיב אובייקט Class. לכן, ה-intent הזה מפעיל באופן מפורש את המחלקה DownloadService באפליקציה.

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

דוגמה ל-Intent משתמע

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

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

Kotlin

// Create the text message with a string.
val sendIntent = Intent().apply {
    action = Intent.ACTION_SEND
    putExtra(Intent.EXTRA_TEXT, textMessage)
    type = "text/plain"
}

// Try to invoke the intent.
try {
    startActivity(sendIntent)
} catch (e: ActivityNotFoundException) {
    // Define what your app should do if no activity can handle the intent.
}

Java

// Create the text message with a string.
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");

// Try to invoke the intent.
try {
    startActivity(sendIntent);
} catch (ActivityNotFoundException e) {
    // Define what your app should do if no activity can handle the intent.
}

כשמתבצעת קריאה ל-startActivity(), המערכת בודקת את כל האפליקציות המותקנות כדי לקבוע אילו מהן יכולות לטפל ב-Intent מהסוג הזה (Intent עם הפעולה ACTION_SEND שנושא נתונים מסוג text/plain). אם יש רק אפליקציה אחת שיכולה לטפל בכוונת המשתמש, האפליקציה הזו נפתחת באופן מיידי ומקבלת את כוונת המשתמש. אם אין אפליקציות אחרות שיכולות לטפל בזה, האפליקציה שלכם יכולה לתפוס את ActivityNotFoundException שמתרחש. אם כמה פעילויות מקבלות את ה-Intent, המערכת מציגה תיבת דו-שיח כמו זו שמוצגת באיור 2, כדי שהמשתמש יוכל לבחור באיזו אפליקציה להשתמש.

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

איור 2. תיבת דו-שיח לבחירה.

איך מאלצים את המערכת להציג את בורר האפליקציות

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

עם זאת, אם כמה אפליקציות יכולות להגיב ל-Intent והמשתמש עשוי לרצות להשתמש באפליקציה אחרת בכל פעם, צריך להציג במפורש תיבת דו-שיח לבחירה. בתיבת הדו-שיח לבחירה, המשתמש מתבקש לבחור באיזו אפליקציה להשתמש לפעולה (המשתמש לא יכול לבחור אפליקציית ברירת מחדל לפעולה). לדוגמה, כשהאפליקציה מבצעת פעולת "שיתוף" עם הפעולה ACTION_SEND, יכול להיות שהמשתמשים ירצו לשתף באמצעות אפליקציה אחרת בהתאם למצב הנוכחי שלהם, לכן תמיד צריך להשתמש בתיבת הדו-שיח לבחירה, כפי שמוצג באיור 2.

כדי להציג את בורר הקבצים, יוצרים Intent באמצעות createChooser() ומעבירים אותו אל startActivity(), כמו שמוצג בדוגמה הבאה. בדוגמה הזו מוצגת תיבת דו-שיח עם רשימה של אפליקציות שמגיבות ל-Intent שמועבר ל-method‏ createChooser(), והטקסט שסופק משמש ככותרת של תיבת הדו-שיח.

Kotlin

val sendIntent = Intent(Intent.ACTION_SEND)
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
val title: String = resources.getString(R.string.chooser_title)
// Create intent to show the chooser dialog
val chooser: Intent = Intent.createChooser(sendIntent, title)

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(packageManager) != null) {
    startActivity(chooser)
}

Java

Intent sendIntent = new Intent(Intent.ACTION_SEND);
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

זיהוי הפעלות לא בטוחות של Intent

יכול להיות שהאפליקציה שלכם תפעיל אובייקטים מסוג Intent כדי לנווט בין רכיבים בתוך האפליקציה, או כדי לבצע פעולה בשם אפליקציה אחרת. כדי לשפר את אבטחת הפלטפורמה, ב-Android 12 (רמת API‏ 31) ואילך יש תכונת ניפוי באגים שמזהירה אתכם אם האפליקציה מפעילה אובייקט Intent באופן לא בטוח. לדוגמה, יכול להיות שהאפליקציה תפעיל באופן לא בטוח Intent בתצוגת עץ, שהוא Intent שמועבר כתוספת ב-Intent אחר.

אם האפליקציה מבצעת את שתי הפעולות הבאות, המערכת מזהה הפעלה לא בטוחה של Intent ומתרחשת הפרה של StrictMode:

  1. האפליקציה מבטלת את ה-parcel של Intent בתצוגת עץ מהתוספות של Intent שנמסר.
  2. האפליקציה מפעילה מיד רכיב אפליקציה באמצעות ה-Intent המוטמע, למשל העברת ה-Intent אל startActivity(),‏ startService() או bindService().

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

בדיקה אם יש הפעלות של Intent לא בטוחים

כדי לבדוק אם יש באפליקציה הפעלות לא בטוחות של Intent, צריך להפעיל את detectUnsafeIntentLaunch() כשמגדירים את VmPolicy, כמו שמוצג בקטע הקוד הבא. אם האפליקציה מזהה הפרה של StrictMode, יכול להיות שתרצו לעצור את הפעלת האפליקציה כדי להגן על מידע שעלול להיות רגיש.

Kotlin

fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build())
}

Java

protected void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build());
}

שימוש אחראי יותר ב-Intent

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

העתיקו רק את הנתונים הנוספים החיוניים בתוך ה-Intent, ובצעו את כל פעולות הניקוי והאימות הנדרשות. יכול להיות שהאפליקציה תעתיק את הנתונים הנוספים מכוונה אחת לכוונה אחרת שמשמשת להפעלת רכיב חדש. זה קורה כשהאפליקציה מפעילה את putExtras(Intent) או את putExtras(Bundle). אם האפליקציה מבצעת אחת מהפעולות האלה, צריך להעתיק רק את הנתונים הנוספים שהרכיב המקבל מצפה להם. אם ה-Intent השני (שמקבל את העותק) מפעיל רכיב שלא מיוצא, צריך לנקות ולאמת את הנתונים הנוספים לפני שמעתיקים אותם ל-Intent שמפעיל את הרכיב.

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

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

התרשים באיור 2 מראה איך המערכת מעבירה את השליטה מהאפליקציה (הלקוח) לאפליקציה אחרת (השירות), ובחזרה לאפליקציה שלכם:

  1. האפליקציה יוצרת Intent שמפעיל פעילות באפליקציה אחרת. בתוך ה-Intent הזה, מוסיפים אובייקט PendingIntent כתוספת. ה-intent בהמתנה הזה מפעיל רכיב באפליקציה שלך, והרכיב הזה לא מיוצא.
  2. כשהאפליקציה השנייה מקבלת את ה-Intent של האפליקציה שלכם, היא מחלצת את האובייקט PendingIntent המוטמע.
  3. האפליקציה השנייה מפעילה את השיטה send() באובייקט PendingIntent.
  4. אחרי שהשליטה חוזרת לאפליקציה, המערכת מפעילה את ה-intent בהמתנה באמצעות ההקשר של האפליקציה.

איור 2. דיאגרמה של תקשורת בין אפליקציות כשמשתמשים ב-intent בהמתנה מקונן.

קבלת Intent מרומז

כדי לפרסם אילו כוונות משתמש משתמעות האפליקציה יכולה לקבל, צריך להצהיר על מסנן Intent אחד או יותר לכל אחד מרכיבי האפליקציה באמצעות רכיב <intent-filter> בקובץ המניפסט. כל מסנן Intent מציין את סוג ה-Intent שהוא מקבל על סמך הפעולה, הנתונים והקטגוריה של ה-Intent. המערכת מעבירה Intent מרומז לרכיב האפליקציה רק אם ה-Intent יכול לעבור דרך אחד ממסנני ה-Intent.

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

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

כל מסנן Intent מוגדר על ידי רכיב <intent-filter> בקובץ המניפסט של האפליקציה, והוא מקונן ברכיב האפליקציה המתאים (כמו רכיב <activity>).

בכל רכיב של האפליקציה שכולל רכיב <intent-filter>, צריך להגדיר ערך מפורש ל-android:exported. המאפיין הזה מציין אם לאפליקציות אחרות יש גישה לרכיב האפליקציה. במצבים מסוימים, כמו פעילויות שמסנני ה-Intent שלהן כוללים את הקטגוריה LAUNCHER, כדאי להגדיר את המאפיין הזה כ-true. אחרת, בטוח יותר להגדיר את המאפיין הזה לערך false.

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

בתוך <intent-filter>, אתם יכולים לציין את סוג ה-Intent שאתם רוצים לקבל באמצעות אחד או יותר משלושת הרכיבים הבאים:

<action>
מצהיר על פעולת ה-Intent שהתקבלה, במאפיין name. הערך חייב להיות ערך המחרוזת המילולי של פעולה, ולא הקבוע של המחלקה.
<data>
מצהיר על סוג הנתונים שהמסנן מקבל, באמצעות מאפיין אחד או יותר שמציינים היבטים שונים של ה-URI של הנתונים (scheme, ‏ host, ‏ port,‏ path) וסוג ה-MIME.
<category>
מצהיר על קטגוריית ה-Intent שהתקבלה, במאפיין name. הערך חייב להיות הערך המילולי של מחרוזת הפעולה, ולא הקבוע של המחלקה.

הערה: כדי לקבל אובייקטים מסוג Intent מרומזים, צריך לכלול את הקטגוריה CATEGORY_DEFAULT במסנן Intent. ה-methods‏ startActivity() ו-startActivityForResult() מתייחסות לכל ה-intent כאילו הן הצהירו על הקטגוריה CATEGORY_DEFAULT. אם לא תצהירו על הקטגוריה הזו במסנן ה-Intent, אף Intent מרומז לא יפתור את הפעילות שלכם.

לדוגמה, הנה הצהרת פעילות עם מסנן Intent לקבלת Intent מסוג ACTION_SEND כשסוג הנתונים הוא טקסט:

<activity android:name="ShareActivity" android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

אפשר ליצור מסנן שכולל יותר ממופע אחד של <action>,‏ <data> או <category>. אם כן, אתם צריכים לוודא שהרכיב יכול לטפל בכל השילובים של רכיבי הסינון האלה.

אם רוצים לטפל בכמה סוגים של Intent, אבל רק בשילובים ספציפיים של פעולה, נתונים וסוג קטגוריה, צריך ליצור כמה מסנני Intent.

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

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

באופן דומה, כדי להימנע מהפעלה לא מכוונת של Service של אפליקציה אחרת, תמיד צריך להשתמש ב-Intent מפורש כדי להפעיל את השירות שלכם.

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

מסננים לדוגמה

כדי להדגים חלק מההתנהגויות של מסנן Intent, הנה דוגמה מקובץ המניפסט של אפליקציה לשיתוף ברשתות חברתיות:

<activity android:name="MainActivity" android:exported="true">
    <!-- This activity is the main entry, should appear in app launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="ShareActivity" android:exported="false">
    <!-- This activity handles "SEND" actions with text data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <action android:name="android.intent.action.SEND_MULTIPLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
    </intent-filter>
</activity>

הפעילות הראשונה, MainActivity, היא נקודת הכניסה הראשית של האפליקציה – הפעילות שנפתחת כשהמשתמש מפעיל את האפליקציה בפעם הראשונה באמצעות סמל מרכז האפליקציות:

  • הפעולה ACTION_MAIN מציינת שזו נקודת הכניסה הראשית, והיא לא מצפה לנתוני Intent.
  • הקטגוריה CATEGORY_LAUNCHER מציינת שצריך למקם את הסמל של הפעילות הזו במרכז האפליקציות של המערכת. אם לא מצוין סמל עם icon ברכיב <activity>, המערכת משתמשת בסמל מהרכיב <application>.

כדי שהפעילות תופיע במרכז האפליקציות, צריך לשייך את שתי הפעילויות האלה.

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

הערה: סוג ה-MIME‏ application/vnd.google.panorama360+jpg הוא סוג נתונים מיוחד שמציין תמונות פנורמיות, שאפשר לטפל בהן באמצעות ממשקי ה-API של Google Panorama.

התאמת Intent למסנני Intent של אפליקציות אחרות

אם אפליקציה אחרת מטרגטת ל-Android 13 (רמת API 33) ומעלה, היא יכולה לטפל ב-Intent של האפליקציה שלכם רק אם ה-Intent תואם לפעולות ולקטגוריות של רכיב <intent-filter> באפליקציה האחרת. אם המערכת לא מוצאת התאמה, היא מפעילה ActivityNotFoundException. האפליקציה השולחת צריכה לטפל בחריגה הזו.

באופן דומה, אם מעדכנים את האפליקציה כך שהיא תטרגט את Android מגרסה 13 ואילך, כל ה-Intent שמקורם באפליקציות חיצוניות מועברים לרכיב מיוצא של האפליקציה רק אם ה-Intent הזה תואם לפעולות ולקטגוריות של רכיב <intent-filter> שהאפליקציה מצהירה עליו. ההתנהגות הזו מתרחשת בלי קשר לגרסת ה-SDK המטורגטת של האפליקציה השולחת.

במקרים הבאים, התאמה לפי כוונה לא נאכפת:

  • רכיבים שלא מצהירים על מסנני Intent מקבלים Intent.
  • אינטנטים שמקורם באותה אפליקציה.
  • כוונה שמקורה במערכת, כלומר כוונה שנשלחת מ"מזהה המשתמש של המערכת" (uid=1000). אפליקציות מערכת כוללות את system_server ואפליקציות שבהן הערך של android:sharedUserId מוגדר כ-android.uid.system.
  • כוונות שמקורן ב-root.

מידע נוסף על התאמת כוונות

שימוש ב-intent בהמתנה

אובייקט PendingIntent הוא מעטפת של אובייקט Intent. המטרה העיקרית של PendingIntent היא לתת הרשאה לאפליקציה זרה להשתמש ב-Intent הכלול כאילו הוא הופעל מהתהליך של האפליקציה שלך.

תרחישי השימוש העיקריים ב-intent בהמתנה כוללים:

  • הצהרה על Intent שצריך להפעיל כשהמשתמש מבצע פעולה עם Notification (NotificationManager של מערכת Android מפעיל את Intent).
  • הצהרה על Intent שצריך להפעיל כשהמשתמש מבצע פעולה באמצעות הווידג'ט של האפליקציה (האפליקציה במסך הבית מפעילה את ה-Intent).
  • הצהרה על כוונה שתבוצע בזמן מסוים בעתיד (AlarmManager של מערכת Android מבצע את Intent).

כמו שכל אובייקט Intent מיועד לטיפול על ידי סוג מסוים של רכיב אפליקציה (Activity,‏ Service או BroadcastReceiver), כך גם צריך ליצור PendingIntent עם אותה מטרה. כשמשתמשים ב-intent בהמתנה, האפליקציה לא מפעילה את ה-intent באמצעות קריאה כמו startActivity(). במקום זאת, צריך להצהיר על סוג הרכיב הרצוי כשיוצרים את ה-PendingIntent, על ידי קריאה ל-method המתאים ליצירה:

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

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

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

ציון יכולת השינוי

אם האפליקציה מטרגטת ל-Android מגרסה 12 ואילך, צריך לציין את יכולת ההשתנות של כל אובייקט PendingIntent שהאפליקציה יוצרת. כדי להצהיר שאובייקט PendingIntent מסוים ניתן או לא ניתן לשינוי, משתמשים בדגל PendingIntent.FLAG_MUTABLE או PendingIntent.FLAG_IMMUTABLE, בהתאמה.

אם האפליקציה מנסה ליצור אובייקט PendingIntent בלי להגדיר אף אחד מהדגלים של יכולת השינוי, המערכת מפעילה IllegalArgumentException וההודעה הבאה מופיעה ב-Logcat:

PACKAGE_NAME: Targeting S+ (version 31 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.

Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.

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

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

Kotlin

val pendingIntent = PendingIntent.getActivity(applicationContext,
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE)

Java

PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE);

עם זאת, בתרחישים מסוימים צריך להשתמש באובייקטים של PendingIntent שניתנים לשינוי:

  • תמיכה בפעולות של תשובה ישירה בהתראות. כדי להשתמש בתשובה ישירה, צריך לשנות את נתוני הקליפ באובייקט PendingIntent שמשויך לתשובה. בדרך כלל, מבקשים את השינוי הזה על ידי העברת FILL_IN_CLIP_DATA כדגל ל-method‏ fillIn().
  • שיוך התראות למסגרת של Android Auto באמצעות מופעים של CarAppExtender.
  • הצבת שיחות בבועות באמצעות מופעים של PendingIntent. אובייקט PendingIntent שניתן לשינוי מאפשר למערכת להחיל את הדגלים הנכונים, כמו FLAG_ACTIVITY_MULTIPLE_TASK ו-FLAG_ACTIVITY_NEW_DOCUMENT.
  • בקשת נתוני מיקום של המכשיר באמצעות קריאה ל-requestLocationUpdates() או לממשקי API דומים. האובייקט הניתן לשינוי PendingIntent מאפשר למערכת להוסיף תוספות Intent שמייצגות אירועים במחזור החיים של המיקום. האירועים האלה כוללים שינוי במיקום וספק שהופך לזמין.
  • תזמון התראות באמצעות AlarmManager. האובייקט הניתן לשינוי PendingIntent מאפשר למערכת להוסיף את ה-intent extra‏ EXTRA_ALARM_COUNT. התוספת הזו מייצגת את מספר הפעמים שהופעלה התראה חוזרת. ה-intent הזה מכיל את ה-extra הזה, ולכן הוא יכול להודיע לאפליקציה באופן מדויק אם התראה חוזרת הופעלה כמה פעמים, למשל כשהמכשיר היה במצב שינה.

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

שימוש ב-intent מפורש ב-intent בהמתנה

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

  1. מוודאים שכל השדות "פעולה", "חבילה" ו"רכיב" של Intent הבסיס מוגדרים.
  2. כדי ליצור אובייקטים מסוג intent בהמתנה, משתמשים ב-FLAG_IMMUTABLE, שנוסף ב-Android 6.0 (רמת API ‏23). הדגל הזה מונע מאפליקציות שקיבלו PendingIntent למלא נכסים שלא מולאו. אם הערך של minSdkVersion באפליקציה הוא 22 או פחות, אפשר לספק בטיחות ותאימות יחד באמצעות הקוד הבא:

    if (Build.VERSION.SDK_INT >= 23) {
      // Create a PendingIntent using FLAG_IMMUTABLE.
    } else {
      // Existing code that creates a PendingIntent.
    }

פתרון הבעיה

כשהמערכת מקבלת Intent מרומז להפעלת פעילות, היא מחפשת את הפעילות הכי מתאימה ל-Intent על ידי השוואה למסנני Intent על סמך שלושה היבטים:

  • פעולה.
  • נתונים (גם URI וגם סוג הנתונים).
  • קטגוריה.

בקטעים הבאים מוסבר איך מתבצעת התאמה בין ה-Intent לרכיבים המתאימים, בהתאם להצהרה על מסנן ה-Intent בקובץ המניפסט של האפליקציה.

בדיקת פעולה

כדי לציין פעולות Intent מקובלות, מסנן Intent יכול להצהיר על אפס רכיבי <action> או יותר, כמו בדוגמה הבאה:

<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>

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

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

בדיקת קטגוריה

כדי לציין קטגוריות Intent מקובלות, מסנן Intent יכול להצהיר על אפס רכיבי <category> או יותר, כמו בדוגמה הבאה:

<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    ...
</intent-filter>

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

הערה: מערכת Android מחילה באופן אוטומטי את הקטגוריה CATEGORY_DEFAULT על כל ה-intent המשתמעים שמועברים אל startActivity() ו-startActivityForResult(). אם אתם רוצים שהפעילות תקבל כוונות מרומזות, היא צריכה לכלול קטגוריה של "android.intent.category.DEFAULT" במסנני ה-Intent שלה, כמו שמוצג בדוגמה הקודמת של <intent-filter>.

בדיקת נתונים

כדי לציין את נתוני ה-Intent שמתקבלים, מסנן Intent יכול להצהיר על אפס רכיבי <data> או יותר, כמו בדוגמה הבאה:

<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    ...
</intent-filter>

כל רכיב <data> יכול לציין מבנה URI וסוג נתונים (סוג מדיה MIME). כל חלק ב-URI הוא מאפיין נפרד: scheme,‏ host,‏ port ו-path:

<scheme>://<host>:<port>/<path>

בדוגמה הבאה מוצגים ערכים אפשריים למאפיינים האלה:

content://com.example.project:200/folder/subfolder/etc

ב-URI הזה, הסכימה היא content, המארח הוא com.example.project, היציאה היא 200 והנתיב הוא folder/subfolder/etc.

כל אחד מהמאפיינים האלה הוא אופציונלי ברכיב <data>, אבל יש תלויות לינאריות:

  • אם לא מציינים סכימה, המערכת מתעלמת מהמארח.
  • אם לא מציינים מארח, המערכת מתעלמת מהיציאה.
  • אם לא מציינים את הסכימה ואת המארח, המערכת מתעלמת מהנתיב.

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

  • אם המסנן מציין רק סכימה, כל מזהי ה-URI עם הסכימה הזו תואמים למסנן.
  • אם מסנן מציין סכימה ורשות אבל לא נתיב, כל מזהי ה-URI עם אותה סכימה ורשות עוברים את המסנן, בלי קשר לנתיבים שלהם.
  • אם מסנן מציין סכימה, רשות ונתיב, רק מזהי URI עם אותה סכימה, רשות ונתיב עוברים את המסנן.

הערה: מפרט הנתיב יכול להכיל כוכבית (*) כתו כללי לחיפוש, כדי לדרוש התאמה חלקית בלבד של שם הנתיב.

בבדיקת הנתונים, המערכת משווה בין ה-URI וסוג ה-MIME ב-Intent לבין ה-URI וסוג ה-MIME שצוינו במסנן. אלה הכללים:

  1. Intent שלא מכיל URI או סוג MIME עובר את הבדיקה רק אם המסנן לא מציין URI או סוגי MIME.
  2. Intent שמכיל URI אבל לא מכיל סוג MIME (לא מפורש ולא כזה שאפשר להסיק מה-URI) עובר את הבדיקה רק אם ה-URI שלו תואם לפורמט ה-URI של המסנן, וגם המסנן לא מציין סוג MIME.
  3. Intent שמכיל סוג MIME אבל לא URI עובר את הבדיקה רק אם המסנן מפרט את אותו סוג MIME ולא מציין פורמט URI.
  4. Intent שמכיל גם URI וגם סוג MIME (מפורש או שניתן להסיק אותו מה-URI) עובר את החלק של סוג ה-MIME בבדיקה רק אם הסוג הזה תואם לסוג שמופיע במסנן. הוא עובר את החלק של ה-URI בבדיקה אם ה-URI שלו תואם ל-URI במסנן, או אם יש לו URI מסוג content: או file: והמסנן לא מציין URI. במילים אחרות, רכיב אמור לתמוך בנתונים מסוג content: ו-file: אם המסנן שלו מפרט רק סוג MIME.

הערה: אם ה-intent מציין URI או סוג MIME, בדיקת הנתונים תיכשל אם אין רכיבי <data> ב-<intent-filter>.

הכלל האחרון, כלל (ד), משקף את הציפייה שרכיבים יוכלו לקבל נתונים מחנויות מקומיות מקובץ או מספק לניהול גישה. לכן, המסננים שלהם יכולים לכלול רק סוג נתונים, ואין צורך לציין במפורש את הסכימות content: ו-file:. בדוגמה הבאה מוצג מקרה אופייני שבו רכיב <data> מורה ל-Android שהרכיב יכול לקבל נתוני תמונה מספק לניהול גישה ולהציג אותם:

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>

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

עוד הגדרה נפוצה היא מסנן עם סכימה וסוג נתונים. לדוגמה, רכיב <data> כמו זה שבהמשך, מורה ל-Android שהרכיב יכול לאחזר נתוני וידאו מהרשת כדי לבצע את הפעולה:

<intent-filter>
    <data android:scheme="http" android:mimeType="video/*" />
    ...
</intent-filter>

התאמת כוונות

אובייקטים מסוג Intent מותאמים למסנני Intent לא רק כדי לגלות רכיב יעד להפעלה, אלא גם כדי לגלות מידע על קבוצת הרכיבים במכשיר. לדוגמה, אפליקציית Home מאכלסת את מרכז האפליקציות על ידי איתור כל ה-Activity עם מסנני Intent שמציינים את הפעולה ACTION_MAIN ואת הקטגוריה CATEGORY_LAUNCHER. ההתאמה תצליח רק אם הפעולות והקטגוריות ב-Intent תואמות למסנן, כפי שמתואר במסמכי התיעוד של המחלקה IntentFilter.

האפליקציה שלכם יכולה להשתמש בהתאמת כוונה באופן דומה למה שקורה באפליקציית Home. ל-PackageManager יש קבוצה של שיטות query...() שמחזירות את כל הרכיבים שיכולים לקבל Intent מסוים, וסדרה דומה של שיטות resolve...() שקובעות מהו הרכיב הכי טוב להגיב ל-Intent. לדוגמה, queryIntentActivities() מחזירה רשימה של כל הפעילויות שיכולות לבצע את ה-intent שהועבר כארגומנט, ו-queryIntentServices() מחזירה רשימה דומה של שירותים. אף אחת מהשיטות לא מפעילה את הרכיבים, אלא רק מציגה את הרכיבים שיכולים להגיב. יש שיטה דומה, queryBroadcastReceivers(), למקלטי שידורים.