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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

חיישן סוג תיאור שימושים נפוצים
TYPE_ACCELEROMETER חומרה מודד את כוח התאוצה במטרים/שניות2 שמופעל על מכשיר כל שלושת הצירים הפיזיים (x, y ו-z), כולל כוח הכבידה. זיהוי תנועה (ניעור, הטיה וכו').
TYPE_AMBIENT_TEMPERATURE חומרה מדידת טמפרטורת החדר במעלות צלזיוס (°C). יש לעיין בהערה שבהמשך. מעקב אחרי טמפרטורות האוויר.
TYPE_GRAVITY תוכנה או חומרה מדידה של כוח הכבידה במטרים/שניות2 שמופעל על מכשיר בכל שלושה צירים פיזיים (x, y, z). זיהוי תנועה (ניעור, הטיה וכו').
TYPE_GYROSCOPE חומרה מודדת את קצב הסיבוב של מכשיר ברדי' סביב כל אחד משלושת צירים פיזיים (x, y ו-z). זיהוי סיבוב (סיבוב, סיבוב וכו').
TYPE_LIGHT חומרה מדד של רמת האור בסביבה (תאורה) ב-lx. שליטה בבהירות המסך.
TYPE_LINEAR_ACCELERATION תוכנה או חומרה מודד את כוח התאוצה במטרים2 כלומר הוחלו על מכשיר ב- כל שלושת הצירים הפיזיים (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
אפשר להשתמש במחלקה הזו כדי ליצור מופע של שירות החיישנים. הכיתה הזו מספקת שיטות שונות לגישה ולרישום של חיישנים, רישום וביטול רישום של אירוע חיישן האזנה, והשגת מידע לגבי כיוון. הסיווג הזה מספק גם מספר קבועי חיישנים שמשמשים לדיווח על הדיוק של החיישנים, להגדרת שיעורי צירוף נתונים ולכיול החיישנים.
Sensor
אפשר להשתמש בכיתה הזו כדי ליצור מופע של חיישן ספציפי. בכיתה הזו ניתן למצוא שיטות שמאפשרות לקבוע את היכולות של החיישן.
SensorEvent
המערכת משתמשת בסיווג הזה כדי ליצור אובייקט אירוע חיישן, שמספק מידע על אירוע מחיישן. אובייקט של אירוע חיישן כולל את המידע הבא: הנתונים הגולמיים של החיישן, סוג החיישן שיצר את האירוע, רמת הדיוק של הנתונים וחותמת הזמן של האירוע אירוע.
SensorEventListener
אפשר להשתמש בממשק הזה כדי ליצור שתי שיטות קריאה חוזרת לקבלת התראות (חיישן אירועים) כשערכי החיישנים משתנים או כשרמת הדיוק של החיישן משתנה.

באפליקציה אופיינית אתם משתמשים בממשקי ה-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 על ידי קריאה לשיטה 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);

בשלב הבא אפשר לקבל רשימה של כל החיישנים במכשיר השיטה 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.
    }
}

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

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

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

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

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

טיפול בתצורות שונות של חיישנים

ב-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. מערכת הקואורדינטות (ביחס למכשיר) שמשמשת את החיישן API.

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

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

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

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

הגבלת קצב של חיישנים

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

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

האמולטור משתמש בחיבור עם מכשיר 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 מדריך לאמולטור.

אין לחסום את השיטה onSensorChanged()

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

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

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

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

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

חשוב לבחור את ההשהיה לפני החיישן

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