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

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

  • איך מתחילים פעילות

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

    אם רוצים לקבל תוצאה מהפעילות כשהיא מסתיימת, צריך להתקשר אל startActivityForResult(). התוצאה מתקבלת כIntent אובייקט נפרד בקריאה החוזרת 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().

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

סוגי כוונות

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

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

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

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

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

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

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

בניית כוונה

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

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

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

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

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

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

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

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

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

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

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

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

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

Kotlin

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

Java

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

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

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

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

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

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

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

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

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

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

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

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

Kotlin

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

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

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

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

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

דוגמה לאובייקט 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);

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

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

דוגמה לאובייקט Intent מרומז

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

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

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). אם יש רק אפליקציה אחת שיכולה לטפל בבקשה, היא תיפתח מיד ותקבל את ה-intent. אם אף אפליקציה אחרת לא יכולה לטפל בה, האפליקציה שלכם יכולה לזהות את ActivityNotFoundException שמתרחש. אם כמה פעילויות מקבלות את הכוונה, המערכת מציגה תיבת דו-שיח כמו זו שמוצגת באיור 2, כדי שהמשתמש יוכל לבחור באיזו אפליקציה להשתמש.

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

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

הפעלת בחירת אפליקציה

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

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

כדי להציג את הכלי לבחירת קבצים, יוצרים Intent באמצעות createChooser() ומעבירים אותו אל startActivity(), כמו בדוגמה הבאה. בדוגמה הזו מוצגת תיבת דו-שיח עם רשימה של אפליקציות שמגיבות ל-Intent שמועבר לשיטה 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);
}

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

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

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

  1. האפליקציה שלך מבטלת את האריזה של intent מוטמע מהתוספים של intent שנמסר.
  2. האפליקציה שלך מפעילה באופן מיידי רכיב של האפליקציה באמצעות הכוונה המקוננת הזו, למשל העברת הכוונה אל startActivity(),‏ startService() או bindService().

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

בדיקה של הפעלות לא בטוחות של כוונות

כדי לבדוק אם יש הפעלות של כוונות לא בטוחות באפליקציה, צריך להתקשר אל 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());
}

שימוש אחראי יותר בכוונות

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

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

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

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

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

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

איור 2. תרשים של תקשורת בין אפליקציות כשמשתמשים ב-pending intent מוטמע.

קבלת אובייקט Intent מרומז

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

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

הערה: כדי לקבל Intents משתמעים, צריך לכלול את הקטגוריה CATEGORY_DEFAULT במסנן ה-Intent. השיטות startActivity() ו-startActivityForResult() מתייחסות לכל הכוונות כאילו הן הוגדרו בקטגוריה CATEGORY_DEFAULT. אם לא תצהירו על הקטגוריה הזו במסנן ה-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>. אם כן, אתם צריכים לוודא שהרכיב יכול לטפל בכל השילובים של רכיבי המסנן האלה.

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

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

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

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

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

דוגמאות למסננים

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

<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 מציינת שזו נקודת הכניסה הראשית, ולא צפויים נתונים של כוונות.
  • הקטגוריה CATEGORY_LAUNCHER מציינת שסמל הפעילות הזו צריך להיות במרכז האפליקציות של המערכת. אם לא מציינים סמל באמצעות icon ברכיב <activity>, המערכת משתמשת בסמל מרכיב <application>.

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

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

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

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

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

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

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

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

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

שימוש בכוונת רכישה בהמתנה

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

תרחישי שימוש עיקריים ל-PendingIntent:

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

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

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

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

מידע נוסף על שימוש ב-pending intents זמין במסמכי התיעוד של כל אחד מתרחישי השימוש הרלוונטיים, כמו במדריכי ה-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.

מומלץ ליצור כוונות בהמתנה שלא ניתן לשנות אותן

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

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 כדגל לשיטה fillIn().
  • שיוך התראות למסגרת Android Auto באמצעות מופעים של CarAppExtender.
  • הצגת שיחות בבועות באמצעות מופעים של PendingIntent. אובייקט PendingIntent שניתן לשינוי מאפשר למערכת להחיל את הסימונים הנכונים, כמו FLAG_ACTIVITY_MULTIPLE_TASK ו-FLAG_ACTIVITY_NEW_DOCUMENT.
  • שליחת קריאה ל-requestLocationUpdates() או לממשקי API דומים כדי לבקש מידע על מיקום המכשיר. אובייקט PendingIntent שניתן לשינוי מאפשר למערכת להוסיף תוספים של כוונות שמייצגים אירועים של מחזור החיים של המיקום. האירועים האלה כוללים שינוי במיקום וזמינות של ספק.
  • תזמון התראות באמצעות AlarmManager. אובייקט PendingIntent שניתן לשינוי מאפשר למערכת להוסיף את התוסף של הכוונה EXTRA_ALARM_COUNT. הנתון הזה מייצג את מספר הפעמים שהופעלה אזעקה חוזרת. התוספת הזו מאפשרת להודיע לאפליקציה בצורה מדויקת אם התראה חוזרת הופעלה כמה פעמים, למשל כשהמכשיר היה במצב שינה.

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

שימוש באובייקטים מפורשים של Intent באובייקטים של Intent בהמתנה

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

  1. בודקים ששדות הפעולה, החבילה והרכיב של Intent הבסיס מוגדרים.
  2. כדי ליצור כוונות בהמתנה, צריך להשתמש ב-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.
    }

זיהוי כוונת המשתמש

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

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

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

כדי לציין קטגוריות של כוונות שאפשר לקבל, מסנן כוונות יכול להצהיר על אפס רכיבי <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 על כל הכוונות המרומזות שמועברות אל startActivity() ו-startActivityForResult(). אם רוצים שהפעילות תקבל מנגנוני Intent משתמעים, צריך לכלול קטגוריה של "android.intent.category.DEFAULT" במסנני ה-Intent שלה, כמו בדוגמה הקודמת של <intent-filter>.

בדיקת נתונים

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

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

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

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

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

הערה: אם כוונת המשתמש מציינת 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 מאכלסת את מרכז האפליקציות על ידי איתור כל הפעילויות עם מסנני Intent שמציינים את הפעולה ACTION_MAIN ואת הקטגוריה CATEGORY_LAUNCHER. ההתאמה תצליח רק אם הפעולות והקטגוריות בכוונת המשתמש יתאימו למסנן, כפי שמתואר במסמכי התיעוד של המחלקה IntentFilter.

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