סקירה כללית על חיישנים

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

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

פלטפורמת Android תומכת בשלוש קטגוריות רחבות של חיישנים:

  • חיישני תנועה

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

  • חיישנים סביבתיים

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

  • חיישני מיקום

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

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

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

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

מבוא לחיישנים

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

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

טבלה 1. סוגי החיישנים שפלטפורמת Android תומכת בהם.

חיישן סוג תיאור שימושים נפוצים
TYPE_ACCELEROMETER חומרה המדד הזה מודד את כוח התאוצה במטרים לשנייה2 שמופעל על מכשיר בכל שלושת הצירים הפיזיים (x,‏ y ו-z), כולל כוח המשיכה. זיהוי תנועה (ניעור, הטיה וכו').
TYPE_AMBIENT_TEMPERATURE חומרה מדידת טמפרטורת החדר במעלות צלזיוס (‎°C). ראו הערה בהמשך. ניטור טמפרטורות האוויר.
TYPE_GRAVITY תוכנה או חומרה מדידת כוח המשיכה ב-m/s2 שמופעל על מכשיר בכל שלושת הצירים הפיזיים (x, ‏ y, ‏ z). זיהוי תנועה (ניעור, הטיה וכו').
TYPE_GYROSCOPE חומרה מדידת קצב הסיבוב של המכשיר ברדיאנים לשנייה (rad/s) סביב כל אחד משלושת הצירים הפיזיים (x,‏ y ו-z). זיהוי סיבוב (ספין, פנייה וכו').
TYPE_LIGHT חומרה מדידת רמת האור בסביבה (עוצמת ההארה) ביחידות לוקס. שליטה בבהירות המסך.
TYPE_LINEAR_ACCELERATION תוכנה או חומרה מדידת כוח התאוצה במטרים לשנייה בריבוע (m/s2) שמופעל על מכשיר בכל שלושת הצירים הפיזיים (x,‏ y ו-z), לא כולל כוח המשיכה. מעקב אחרי תאוצה לאורך ציר יחיד.
TYPE_MAGNETIC_FIELD חומרה מודד את השדה הגיאומגנטי הסביבתי לכל שלושת הצירים הפיזיים (x, ‏ y, ‏ z) במיקרוטסלה (μT). יצירת מצפן.
TYPE_ORIENTATION תוכנות מדידת מעלות הסיבוב שהמכשיר מבצע סביב כל שלושת הצירים הפיזיים (x, ‏ y, ‏ z). החל מרמת API 3, אפשר לקבל את מטריצת ההטיה ואת מטריצת הסיבוב של מכשיר באמצעות חיישן הכבידה וחיישן השדה הגיאומגנטי בשילוב עם השיטה getRotationMatrix(). קביעת המיקום של המכשיר.
TYPE_PRESSURE חומרה מדידת לחץ האוויר הסביבתי ב-hPa או ב-mbar. מעקב אחרי שינויים בלחץ האוויר.
TYPE_PROXIMITY חומרה המדד הזה מודד את הקרבה של אובייקט בסנטימטרים ביחס למסך התצוגה של המכשיר. החיישן הזה משמש בדרך כלל כדי לקבוע אם המכשיר מוחזק ליד האוזן של אדם. המיקום של הטלפון במהלך השיחה.
TYPE_RELATIVE_HUMIDITY חומרה מדידה של הלחות היחסית בסביבה באחוזים (%). מעקב אחרי נקודת הטל, הלחות המוחלטת והלחות היחסית.
TYPE_ROTATION_VECTOR תוכנה או חומרה מדידת הכיוון של המכשיר על ידי שלושת הרכיבים של וקטור הסיבוב של המכשיר. זיהוי תנועה וזיהוי סיבוב.
TYPE_TEMPERATURE חומרה מדידת הטמפרטורה של המכשיר במעלות צלזיוס (°C). ההטמעה של החיישן הזה משתנה בין מכשירים שונים, והחיישן הזה הוחלף בחיישן TYPE_AMBIENT_TEMPERATURE ב-API ברמה 14 מעקב אחרי הטמפרטורות.

מסגרת חיישנים

אתם יכולים לגשת לחיישנים האלה ולקבל נתוני חיישנים גולמיים באמצעות מסגרת החיישנים של Android. מסגרת החיישנים היא חלק מחבילת android.hardware והיא כוללת את המחלקות והממשקים הבאים:

SensorManager
אפשר להשתמש במחלקה הזו כדי ליצור מופע של שירות החיישן. המחלקות האלה מספקות שיטות שונות לגישה לחיישנים ולרישום שלהם, לרישום של listeners לאירועי חיישנים ולביטול הרישום שלהם, ולקבלת מידע על אוריינטציה. בנוסף, המחלקה הזו מספקת כמה קבועים של חיישנים שמשמשים לדיווח על דיוק החיישנים, להגדרת קצב איסוף הנתונים ולכיול החיישנים.
Sensor
אפשר להשתמש במחלקה הזו כדי ליצור מופע של חיישן ספציפי. המחלק הזה מספק שיטות שונות שמאפשרות לקבוע את היכולות של חיישן.
SensorEvent
המערכת משתמשת במחלקה הזו כדי ליצור אובייקט של אירוע חיישן, שמספק מידע על אירוע חיישן. אובייקט של אירוע חיישן כולל את המידע הבא: נתוני החיישן הגולמיים, סוג החיישן שיצר את האירוע, רמת הדיוק של הנתונים וחותמת הזמן של האירוע.
SensorEventListener
אפשר להשתמש בממשק הזה כדי ליצור שתי שיטות קריאה חוזרת (callback) שמקבלות התראות (אירועי חיישן) כשערכי החיישן משתנים או כשדיוק החיישן משתנה.

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

  • זיהוי חיישנים ויכולות החיישנים

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

  • מעקב אחרי אירועים של חיישנים

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

זמינות החיישן

הזמינות של החיישנים משתנה ממכשיר למכשיר, אבל היא יכולה להשתנות גם בין גרסאות שונות של Android. הסיבה לכך היא שהחיישנים של Android הוצגו במהלך כמה מהדורות של הפלטפורמה. לדוגמה, הרבה חיישנים הוצגו ב-Android 1.5 (רמת API‏ 3), אבל חלקם לא הוטמעו ולא היו זמינים לשימוש עד Android 2.3 (רמת API‏ 9). באופן דומה, כמה חיישנים הוצגו ב-Android 2.3 (רמת API ‏9) וב-Android 4.0 (רמת API ‏14). שני חיישנים הוצאו משימוש והוחלפו בחיישנים חדשים וטובים יותר.

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

טבלה 2. זמינות החיישנים לפי פלטפורמה.

חיישן ‫Android 4.0
(רמת API‏ 14)
‫Android 2.3‏
(רמת API‏ 9)
‫Android 2.2
(רמת API‏ 8)
‫Android 1.5
(רמת API‏ 3)
TYPE_ACCELEROMETER כן כן כן כן
TYPE_AMBIENT_TEMPERATURE כן לא רלוונטי לא רלוונטי לא רלוונטי
TYPE_GRAVITY כן כן לא רלוונטי לא רלוונטי
TYPE_GYROSCOPE כן כן לא רלוונטי1 לא רלוונטי1
TYPE_LIGHT כן כן כן כן
TYPE_LINEAR_ACCELERATION כן כן לא רלוונטי לא רלוונטי
TYPE_MAGNETIC_FIELD כן כן כן כן
TYPE_ORIENTATION כן2 כן2 כן2 כן
TYPE_PRESSURE כן כן לא רלוונטי1 לא רלוונטי1
TYPE_PROXIMITY כן כן כן כן
TYPE_RELATIVE_HUMIDITY כן לא רלוונטי לא רלוונטי לא רלוונטי
TYPE_ROTATION_VECTOR כן כן לא רלוונטי לא רלוונטי
TYPE_TEMPERATURE כן2 כן כן כן

1 סוג החיישן הזה נוסף ב-Android 1.5 (רמת API ‏3), אבל הוא לא היה זמין לשימוש עד Android 2.3 (רמת API ‏9).

2 החיישן הזה זמין, אבל הוא הוצא משימוש.

זיהוי חיישנים ויכולות חיישנים

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

כדי לזהות את החיישנים במכשיר, קודם צריך לקבל הפניה לשירות החיישנים. כדי לעשות זאת, יוצרים מופע של המחלקה SensorManager על ידי קריאה ל-method‏ getSystemService() והעברת הארגומנט SENSOR_SERVICE. לדוגמה:

Kotlin

private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

Java

private SensorManager sensorManager;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

אחר כך, אפשר לקבל רשימה של כל החיישנים במכשיר על ידי קריאה ל-method‏ getSensorList() ושימוש בקבוע TYPE_ALL. לדוגמה:

Kotlin

val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)

Java

List<Sensor> deviceSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);

אם רוצים להציג רשימה של כל החיישנים מסוג מסוים, אפשר להשתמש בקבוע אחר במקום TYPE_ALL, כמו TYPE_GYROSCOPE, ‏ TYPE_LINEAR_ACCELERATION או TYPE_GRAVITY.

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

Kotlin

private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
if (sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null) {
    // Success! There's a magnetometer.
} else {
    // Failure! No magnetometer.
}

Java

private SensorManager sensorManager;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){
    // Success! There's a magnetometer.
} else {
    // Failure! No magnetometer.
}

הערה: מערכת Android לא מחייבת את יצרני המכשירים לשלב סוגים מסוימים של חיישנים במכשירים שלהם שמבוססים על Android, ולכן יכולים להיות למכשירים מגוון רחב של תצורות חיישנים.

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

שתי שיטות ציבוריות שימושיות במיוחד אם רוצים לבצע אופטימיזציה של האפליקציה עבור חיישנים של יצרנים שונים או גרסאות שונות של חיישן. לדוגמה, אם האפליקציה שלכם צריכה לעקוב אחרי תנועות של המשתמש כמו הטיה וניעור, תוכלו ליצור קבוצה אחת של כללים לסינון נתונים ואופטימיזציות למכשירים חדשים יותר שיש להם חיישן כוח משיכה של ספק מסוים, וקבוצה אחרת של כללים לסינון נתונים ואופטימיזציות למכשירים שאין להם חיישן כוח משיכה ויש להם רק מד תאוצה. בדוגמת הקוד הבאה מוצגות השיטות getVendor() ו-getVersion() שבהן אפשר להשתמש כדי לעשות זאת. בדוגמה הזו, אנחנו מחפשים חיישן כוח משיכה שרשום בו Google LLC כספק ומספר הגרסה שלו הוא 3. אם החיישן הספציפי הזה לא קיים במכשיר, ננסה להשתמש במד התאוצה.

Kotlin

private lateinit var sensorManager: SensorManager
private var mSensor: Sensor? = null

...

sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

if (sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) != null) {
    val gravSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_GRAVITY)
    // Use the version 3 gravity sensor.
    mSensor = gravSensors.firstOrNull { it.vendor.contains("Google LLC") && it.version == 3 }
}
if (mSensor == null) {
    // Use the accelerometer.
    mSensor = if (sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null) {
        sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
    } else {
        // Sorry, there are no accelerometers on your device.
        // You can't play this game.
        null
    }
}

Java

private SensorManager sensorManager;
private Sensor mSensor;

...

sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = null;

if (sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) != null){
    List<Sensor> gravSensors = sensorManager.getSensorList(Sensor.TYPE_GRAVITY);
    for(int i=0; i<gravSensors.size(); i++) {
        if ((gravSensors.get(i).getVendor().contains("Google LLC")) &&
           (gravSensors.get(i).getVersion() == 3)){
            // Use the version 3 gravity sensor.
            mSensor = gravSensors.get(i);
        }
    }
}
if (mSensor == null){
    // Use the accelerometer.
    if (sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null){
        mSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    } else{
        // Sorry, there are no accelerometers on your device.
        // You can't play this game.
    }
}

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

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

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

מעקב אחרי אירועים של חיישנים

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

  • רמת הדיוק של חיישן משתנה.

    במקרה כזה, המערכת מפעילה את השיטה onAccuracyChanged() ומספקת הפניה לאובייקט Sensor שהשתנה ולדיוק החדש של החיישן. רמת הדיוק מיוצגת על ידי אחת מארבע קבועי הסטטוס הבאים: SENSOR_STATUS_ACCURACY_LOW, SENSOR_STATUS_ACCURACY_MEDIUM, SENSOR_STATUS_ACCURACY_HIGH, או SENSOR_STATUS_UNRELIABLE.

  • חיישן מדווח על ערך חדש.

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

בדוגמת הקוד הבאה מוצג אופן השימוש בשיטה onSensorChanged() כדי לעקוב אחרי נתונים מחיישן האור. בדוגמה הזו מוצגים נתוני החיישן הגולמיים ב-TextView שמוגדר בקובץ main.xml כ-sensor_data.

Kotlin

class SensorActivity : Activity(), SensorEventListener {
    private lateinit var sensorManager: SensorManager
    private var mLight: Sensor? = null

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        mLight = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        // Do something here if sensor accuracy changes.
    }

    override fun onSensorChanged(event: SensorEvent) {
        // The light sensor returns a single value.
        // Many sensors return 3 values, one for each axis.
        val lux = event.values[0]
        // Do something with this sensor value.
    }

    override fun onResume() {
        super.onResume()
        mLight?.also { light ->
            sensorManager.registerListener(this, light, SensorManager.SENSOR_DELAY_NORMAL)
        }
    }

    override fun onPause() {
        super.onPause()
        sensorManager.unregisterListener(this)
    }
}

Java

public class SensorActivity extends Activity implements SensorEventListener {
    private SensorManager sensorManager;
    private Sensor mLight;

    @Override
    public final void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mLight = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
    }

    @Override
    public final void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Do something here if sensor accuracy changes.
    }

    @Override
    public final void onSensorChanged(SensorEvent event) {
        // The light sensor returns a single value.
        // Many sensors return 3 values, one for each axis.
        float lux = event.values[0];
        // Do something with this sensor value.
    }

    @Override
    protected void onResume() {
        super.onResume();
        sensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onPause() {
        super.onPause();
        sensorManager.unregisterListener(this);
    }
}

בדוגמה הזו, השהיית הנתונים שמוגדרת כברירת מחדל (SENSOR_DELAY_NORMAL) מצוינת כשמפעילים את השיטה registerListener(). ההגדרה של עיכוב הנתונים (או קצב הדגימה) קובעת את המרווח שבו אירועים של חיישנים נשלחים לאפליקציה באמצעות שיטת הקריאה החוזרת onSensorChanged(). השהיית הנתונים שמוגדרת כברירת מחדל מתאימה למעקב אחרי שינויים אופייניים בכיוון המסך, והיא נמשכת 200,000 מיקרו-שניות. אפשר לציין השהיות אחרות בנתונים, כמו SENSOR_DELAY_GAME (השהיה של 20,000 מיקרו-שניות), SENSOR_DELAY_UI (השהיה של 60,000 מיקרו-שניות) או SENSOR_DELAY_FASTEST (השהיה של 0 מיקרו-שניות). החל מ-Android 3.0 (רמת API‏ 11), אפשר גם לציין את העיכוב כערך אבסולוטי (במיקרו-שניות).

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

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

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

טיפול בהגדרות שונות של חיישנים

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

יש שתי אפשרויות לוודא שחיישן מסוים קיים במכשיר:

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

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

זיהוי חיישנים בזמן ריצה

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

Kotlin

private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

if (sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null) {
    // Success! There's a pressure sensor.
} else {
    // Failure! No pressure sensor.
}

Java

private SensorManager sensorManager;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null){
    // Success! There's a pressure sensor.
} else {
    // Failure! No pressure sensor.
}

שימוש במסננים של Google Play כדי לטרגט הגדרות ספציפיות של חיישנים

אם אתם מפרסמים את האפליקציה שלכם ב-Google Play, אתם יכולים להשתמש ברכיב <uses-feature> בקובץ המניפסט כדי לסנן את האפליקציה ממכשירים שאין להם את תצורת החיישן המתאימה לאפליקציה שלכם. רכיב ה-<uses-feature> כולל כמה מתארי חומרה שמאפשרים לסנן אפליקציות על סמך נוכחות של חיישנים ספציפיים. החיישנים שאפשר לכלול ברשימה הם: מד תאוצה, ברומטר, מצפן (שדה גיאומגנטי), ג'ירוסקופ, אור וקרבה. הדוגמה הבאה מציגה רשומה במניפסט שמסננת אפליקציות שלא כוללות מד תאוצה:

<uses-feature android:name="android.hardware.sensor.accelerometer"
              android:required="true" />

אם מוסיפים את הרכיב והמתאר הזה למניפסט של האפליקציה, המשתמשים יראו את האפליקציה ב-Google Play רק אם במכשיר שלהם יש מד תאוצה.

צריך להגדיר את התיאור ל-android:required="true" רק אם האפליקציה מסתמכת באופן מלא על חיישן ספציפי. אם האפליקציה משתמשת בחיישן כדי להפעיל פונקציונליות מסוימת, אבל עדיין פועלת בלי החיישן, צריך לציין את החיישן ברכיב <uses-feature>, אבל להגדיר את המתאר ל-android:required="false". כך אפשר לוודא שהמכשירים יוכלו להתקין את האפליקציה גם אם אין בהם את החיישן הספציפי הזה. זו גם שיטה מומלצת לניהול פרויקטים, שעוזרת לעקוב אחרי התכונות שבהן נעשה שימוש באפליקציה. חשוב לזכור: אם האפליקציה משתמשת בחיישן מסוים, אבל עדיין פועלת בלעדיו, צריך לזהות את החיישן בזמן הריצה ולהשבית או להפעיל את התכונות של האפליקציה בהתאם.

מערכת קואורדינטות של חיישן

באופן כללי, מסגרת החיישנים משתמשת במערכת קואורדינטות סטנדרטית של 3 צירים כדי להציג ערכי נתונים. ברוב החיישנים, מערכת הקואורדינטות מוגדרת ביחס למסך המכשיר כשהמכשיר מוחזק בכיוון ברירת המחדל שלו (ראו איור 1). כשמחזיקים את המכשיר בכיוון ברירת המחדל שלו, ציר X הוא אופקי ומצביע ימינה, ציר Y הוא אנכי ומצביע כלפי מעלה, וציר Z מצביע כלפי חוץ של פני המסך. במערכת הזו, לקואורדינטות שמאחורי המסך יש ערכי Z שליליים. מערכת הקואורדינטות הזו משמשת את החיישנים הבאים:

איור 1. מערכת קואורדינטות (ביחס למכשיר) שמשמשת את Sensor API.

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

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

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

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

הגבלת קצב של יצירת בקשות בחיישן

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

מגבלת קצב הרענון תלויה באופן הגישה לנתוני החיישנים:

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

AndroidManifest.xml

<manifest ...>
    <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"/>
    <application ...>
        ...
    </application>
</manifest>

שיטות מומלצות לגישה לחיישנים ולשימוש בהם

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

איסוף נתוני חיישנים רק כשהאפליקציה פועלת בחזית

במכשירים שמותקנת בהם מערכת Android מגרסה 9 (API ברמה 28) ומעלה, לאפליקציות שפועלות ברקע יש את ההגבלות הבאות:

  • חיישנים שמשתמשים במצב דיווח רציף, כמו מדי תאוצה וג'ירוסקופים, לא מקבלים אירועים.
  • חיישנים שמשתמשים במצבי הדיווח on-change או one-shot לא מקבלים אירועים.

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

ביטול הרישום של מעבדי אירועים של חיישנים

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

Kotlin

private lateinit var sensorManager: SensorManager
...
override fun onPause() {
    super.onPause()
    sensorManager.unregisterListener(this)
}

Java

private SensorManager sensorManager;
...
@Override
protected void onPause() {
    super.onPause();
    sensorManager.unregisterListener(this);
}

מידע נוסף זמין unregisterListener(SensorEventListener).

בדיקה באמצעות אמולטור Android

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

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

אפשר לראות את קוד המקור של האפליקציה SdkControllerSensor במיקום הבא:

$ your-android-sdk-directory/tools/apps/SdkController

כדי להעביר נתונים בין המכשיר שלכם לבין האמולטור, פועלים לפי השלבים הבאים:

  1. מוודאים שניפוי באגים ב-USB מופעל במכשיר.
  2. מחברים את המכשיר למחשב הפיתוח באמצעות כבל USB.
  3. מפעילים את האפליקציה SdkControllerSensor במכשיר.
  4. באפליקציה, בוחרים את החיישנים שרוצים ליצור להם אמולציה.
  5. מריצים את הפקודה adb הבאה:

  6. $ adb forward tcp:1968 tcp:1968
    
  7. מפעילים את האמולטור. עכשיו תוכלו להחיל טרנספורמציות על האמולטור על ידי הזזת המכשיר.

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

מידע נוסף זמין במדריך לאמולטור של Android.

לא לחסום את ה-method‏ onSensorChanged()‎

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

לא מומלץ להשתמש בשיטות או בסוגי חיישנים שהוצאו משימוש

כמה שיטות וקבועים הוצאו משימוש. בפרט, סוג החיישן הוצא משימוש.TYPE_ORIENTATION כדי לקבל נתוני אוריינטציה, צריך להשתמש במקום זאת בשיטה getOrientation(). באופן דומה, סוג החיישן TYPE_TEMPERATURE הוצא משימוש. במכשירים עם Android 4.0, צריך להשתמש במקום זאת בחיישן מסוג TYPE_AMBIENT_TEMPERATURE.

אימות החיישנים לפני השימוש בהם

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

בחירה קפדנית של עיכובים בחיישן

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