ברוב המכשירים עם Android יש חיישנים מובנים שמודדים תנועה, כיוון ותנאי סביבה שונים. החיישנים האלה יכולים לספק נתונים גולמיים ברמת דיוק גבוהה, והם שימושיים אם רוצים לעקוב אחרי התנועה או המיקום של המכשיר בתלת-ממד, או אם רוצים לעקוב אחרי שינויים בסביבה הקרובה למכשיר. לדוגמה, משחק יכול לעקוב אחרי קריאות מחיישן כוח הכבידה של המכשיר כדי להסיק תנועות מורכבות של המשתמש, כמו הטיה, ניעור, סיבוב או נדנוד. באופן דומה, אפליקציית מזג אוויר יכולה להשתמש בחיישן הטמפרטורה ובחיישן הלחות של המכשיר כדי לחשב ולדווח על נקודת הטל, ואפליקציית נסיעות יכולה להשתמש בחיישן השדה הגיאומגנטי ובמד התאוצה כדי לדווח על כיוון המצפן.
מקורות מידע שקשורים לנושא:
פלטפורמת Android תומכת בשלוש קטגוריות רחבות של חיישנים:
- חיישני תנועה
החיישנים האלה מודדים את כוחות התאוצה והסיבוב לאורך שלושה צירים. הקטגוריה הזו כוללת מדי תאוצה, חיישני כבידה, גירוסקופים וחיישני וקטור סיבוב.
- חיישנים סביבתיים
החיישנים האלה מודדים פרמטרים סביבתיים שונים, כמו טמפרטורת האוויר בסביבה, לחץ, תאורה ולחות. הקטגוריה הזו כוללת ברומטרים, פוטומטרים ומדחומים.
- חיישני מיקום
החיישנים האלה מודדים את המיקום הפיזי של המכשיר. הקטגוריה הזו כוללת חיישני כיוון ומגנטומטרים.
אתם יכולים לגשת לחיישנים שזמינים במכשיר ולקבל נתוני חיישנים גולמיים באמצעות מסגרת החיישנים של Android. מסגרת החיישנים מספקת כמה מחלקות וממשקים שעוזרים לכם לבצע מגוון רחב של משימות שקשורות לחיישנים. לדוגמה, אתם יכולים להשתמש ב-sensor framework כדי:
- לבדוק אילו חיישנים זמינים במכשיר.
- לזהות את היכולות של חיישן ספציפי, כמו הטווח המקסימלי, היצרן, דרישות ההספק והרזולוציה.
- לקבל נתוני חיישנים גולמיים ולהגדיר את הקצב המינימלי לקבלת נתוני חיישנים.
- לרשום ולבטל רישום של רכיבי event listener של חיישנים שעוקבים אחרי שינויים בחיישנים.
במאמר הזה מפורטים החיישנים שזמינים בפלטפורמת Android. הוא גם מספק מבוא למסגרת החיישנים.
מבוא לחיישנים
מסגרת החיישנים של Android מאפשרת לכם לגשת לסוגים רבים של חיישנים. חלק מהחיישנים האלה מבוססים על חומרה וחלק מבוססים על תוכנה. חיישנים מבוססי חומרה הם רכיבים פיזיים שמובנים בטלפון או בטאבלט. הם מפיקים את הנתונים שלהם על ידי מדידה ישירה של מאפיינים סביבתיים ספציפיים, כמו תאוצה, עוצמת השדה הגיאומגנטי או שינוי זוויתי. חיישנים מבוססי תוכנה הם לא מכשירים פיזיים, אבל הם מחקים חיישנים מבוססי חומרה. חיישנים מבוססי תוכנה מפיקים נתונים מחיישן אחד או יותר שמבוסס על חומרה, ולפעמים הם נקראים חיישנים וירטואליים או חיישנים סינתטיים. חיישן התאוצה הלינארית וחיישן כוח הכבידה הם דוגמאות לחיישנים מבוססי תוכנה. בטבלה 1 מפורטים החיישנים שנתמכים בפלטפורמת Android.
רק בחלק קטן מהמכשירים עם Android יש את כל סוגי החיישנים. לדוגמה, לרוב מכשירי הטלפון והטאבלטים יש מד תאוצה ומגנטומטר, אבל לפחות מכשירים יש ברומטרים או מדי חום. בנוסף, במכשיר יכולים להיות כמה חיישנים מאותו סוג. לדוגמה, למכשיר יכולים להיות שני חיישני כבידה, ולכל אחד מהם יש טווח שונה.
טבלה 1. סוגי החיישנים שנתמכים בפלטפורמת Android.
| חיישן | סוג | תיאור | שימושים נפוצים |
|---|---|---|---|
TYPE_ACCELEROMETER |
חומרה | מודד את כוח התאוצה ביחידות m/s2 שמופעל על מכשיר בכל שלושת הצירים הפיזיים (X, Y ו-Z), כולל כוח הכבידה. | זיהוי תנועה (ניעור, הטיה וכו'). |
TYPE_AMBIENT_TEMPERATURE |
חומרה | מודד את טמפרטורת החדר במעלות צלזיוס (°C). ראו הערה בהמשך. | מעקב אחרי טמפרטורות האוויר. |
TYPE_GRAVITY |
תוכנה או חומרה | מודד את כוח הכבידה ב-m/s2 שמופעל על מכשיר בכל שלושת הצירים הפיזיים (X, Y, Z). | זיהוי תנועה (ניעור, הטיה וכו'). |
TYPE_GYROSCOPE |
חומרה | מודד את קצב הסיבוב של המכשיר ברדיאנים לשנייה (rad/s) סביב כל אחד משלושת הצירים הפיזיים (X, Y ו-Z). | זיהוי סיבוב (סיבוב, פנייה וכו'). |
TYPE_LIGHT |
חומרה | מודד את רמת האור מהסביבה (תאורה) ב-lx. | שליטה בבהירות המסך. |
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- אפשר להשתמש במחלקה הזו כדי ליצור מופע של שירות החיישן. המחלקה הזו מספקת שיטות שונות לגישה לחיישנים ולרישום שלהם, לרישום ולביטול הרישום של רכיבי event listener של חיישנים, ולקבלת מידע על הכיוון. המחלקה הזו מספקת גם כמה קבועים של חיישנים שמשמשים לדיווח על דיוק החיישנים, להגדרת קצבי איסוף הנתונים ולכיול החיישנים.
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 על ידי קריאה ל-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.
אפשר גם לבדוק אם סוג ספציפי של חיישן קיים במכשיר באמצעות ה-method getDefaultSensor() והעברת הקבוע של הסוג לחיישן ספציפי. אם במכשיר יש יותר מחיישן אחד מסוג מסוים, אחד מהחיישנים צריך להיות מוגדר כחיישן ברירת המחדל. אם אין חיישן ברירת מחדל לסוג מסוים של חיישן, הפעלת ה-method מחזירה ערך 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, כך שיכול להיות מגוון רחב של הגדרות חיישנים במכשירים.
בנוסף להצגת רשימה של החיישנים שקיימים במכשיר, אתם יכולים להשתמש ב-methods הציבוריות של המחלקה Sensor כדי לקבוע את היכולות והמאפיינים של חיישנים ספציפיים. האפשרות הזו שימושית אם רוצים שהאפליקציה תפעל בצורה שונה בהתאם לחיישנים או ליכולות החיישנים שזמינים במכשיר. לדוגמה, אפשר להשתמש בשיטות getResolution() ו-getMaximumRange() כדי לקבל את הרזולוציה ואת טווח המדידה המקסימלי של החיישן. אפשר גם להשתמש ב-method 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(), שמחזירה את מרווח הזמן המינימלי (במיקרו-שניות) שחיישן יכול להשתמש בו כדי לחוש נתונים. כל חיישן שמחזיר ערך שאינו אפס ל-method 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. - חיישן מדווח על ערך חדש.
במקרה כזה, המערכת מפעילה את ה-method
onSensorChanged()ומספקת לכם אובייקטSensorEvent. אובייקטSensorEventמכיל מידע על נתוני החיישן החדשים, כולל: דיוק הנתונים, החיישן שיצר את הנתונים, חותמת הזמן שבה הנתונים נוצרו והנתונים החדשים שהחיישן תיעד.
הקוד הבא מראה איך להשתמש ב-method 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() מופעלת. השהיית הנתונים (או תדירות הדגימה) קובעת את המרווח שבו אירועי החיישן נשלחים לאפליקציה באמצעות שיטת הקריאה החוזרת (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() כדי לרשום ולבטל את הרישום של פונקציית ה-event listener של החיישן. מומלץ תמיד להשבית חיישנים שלא צריך, במיוחד כשהפעילות מושהית. אם לא תעשו את זה, הסוללה עלולה להתרוקן תוך כמה שעות, כי חלק מהחיישנים צורכים הרבה אנרגיה. המערכת לא תשבית את החיישנים באופן אוטומטי כשהמסך נכבה.
טיפול בהגדרות שונות של חיישנים
ב-Android אין הגדרת חיישן סטנדרטית למכשירים, ולכן יצרני המכשירים יכולים לשלב כל הגדרת חיישן שהם רוצים במכשירים שלהם שמבוססים על Android. כתוצאה מכך, מכשירים יכולים לכלול מגוון חיישנים במגוון רחב של הגדרות אישיות. אם האפליקציה שלכם מסתמכת על סוג מסוים של חיישן, אתם צריכים לוודא שהחיישן קיים במכשיר כדי שהאפליקציה תפעל כמו שצריך.
יש שתי אפשרויות לוודא שחיישן מסוים קיים במכשיר:
- לזהות חיישנים בזמן הריצה ולהפעיל או להשבית תכונות של האפליקציה בהתאם.
- אפשר להשתמש במסננים של Google Play כדי לטרגט מכשירים עם הגדרות חיישנים ספציפיות.
כל אחת מהאפשרויות מפורטת בקטעים הבאים.
זיהוי חיישנים בזמן ריצה
אם האפליקציה משתמשת בסוג מסוים של חיישן, אבל לא מסתמכת עליו, אתם יכולים להשתמש ב-sensor framework כדי לזהות את החיישן בזמן הריצה, ואז להשבית או להפעיל את התכונות של האפליקציה בהתאם. לדוגמה, אפליקציית ניווט יכולה להשתמש בחיישן הטמפרטורה, בחיישן הלחץ, בחיישן ה-GPS ובחיישן השדה הגיאומגנטי כדי להציג את הטמפרטורה, הלחץ הברומטרי, המיקום והכיוון במצפן. אם במכשיר אין חיישן לחץ, אפשר להשתמש ב-sensor framework כדי לזהות את היעדר החיישן בזמן הריצה, ואז להשבית את החלק בממשק המשתמש של האפליקציה שמציג את הלחץ. לדוגמה, הקוד הבא בודק אם יש חיישן לחץ במכשיר:
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.
עוד נקודה שחשוב להבין היא שהאפליקציה לא צריכה להניח שכיוון המסך הטבעי (ברירת המחדל) של המכשיר הוא לאורך. האוריינטציה הטבעית של הרבה מכשירי טאבלט היא לרוחב. מערכת הקואורדינטות של החיישן תמיד מבוססת על הכיוון הטבעי של המכשיר.
לבסוף, אם האפליקציה מתאימה את נתוני החיישן לתצוגה במסך, צריך להשתמש ב-method getRotation() כדי לקבוע את סיבוב המסך, ואז להשתמש ב-method remapCoordinateSystem() כדי למפות את הקואורדינטות של החיישן לקואורדינטות של המסך. צריך לעשות את זה גם אם במניפסט מצוין שהתצוגה היא לאורך בלבד.
הערה: בחלק מהחיישנים והשיטות נעשה שימוש במערכת קואורדינטות שמתייחסת למערכת הייחוס העולמית (בניגוד למערכת הייחוס של המכשיר). החיישנים והשיטות האלה מחזירים נתונים שמייצגים את תנועת המכשיר או את מיקום המכשיר ביחס לכדור הארץ. מידע נוסף זמין בשיטה getOrientation(), בשיטה getRotationMatrix(), בחיישן הכיוון ובחיישן וקטור הסיבוב.
הגבלת הקצב של יצירת בקשות לחיישנים
כדי להגן על מידע רגיש של משתמשים, אם האפליקציה מטרגטת את Android 12 (רמת API 31) ומעלה, המערכת מגבילה את קצב הרענון של הנתונים מחיישני תנועה וחיישני מיקום מסוימים. הנתונים האלה כוללים ערכים שנמדדו על ידי מד התאוצה, הג'ירוסקופ וחיישן השדה הגיאומגנטי של המכשיר.
מגבלת קצב הרענון תלויה באופן הגישה לנתוני החיישנים:
- אם מפעילים את השיטה
registerListener()כדי לעקוב אחרי אירועי חיישנים, תדירות הדגימה של החיישן מוגבלת ל-200Hz. זה נכון לכל הווריאציות של השיטהregisterListener(). - אם משתמשים במחלקה
SensorDirectChannel, תדירות הדגימה של החיישן מוגבלת ל-RATE_NORMAL, שהיא בדרך כלל כ-50Hz.
אם האפליקציה צריכה לאסוף נתונים מחיישני תנועה בקצב גבוה יותר, צריך להצהיר על ההרשאה HIGH_SAMPLING_RATE_SENSORS, כמו שמוצג בקטע הקוד הבא. אחרת, אם האפליקציה תנסה לאסוף נתונים מחיישן התנועה בקצב גבוה יותר בלי להצהיר על ההרשאה הזו, תתרחש שגיאה מסוג SecurityException.
AndroidManifest.xml
<manifest ...> <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"/> <application ...> ... </application> </manifest>
שיטות מומלצות לגישה לחיישנים ולשימוש בהם
כשאתם מתכננים את הטמעת החיישן, חשוב לפעול לפי ההנחיות שמפורטות בקטע הזה. ההנחיות האלה הן שיטות מומלצות לכל מי שמשתמש ב-framework של החיישנים כדי לגשת לחיישנים ולקבל נתונים מהחיישנים.
איסוף נתונים מהחיישנים רק כשהאפליקציה בחזית
במכשירים עם Android 9 (רמת API 28) ומעלה, יש את ההגבלות הבאות על אפליקציות שפועלות ברקע:
- חיישנים שמשתמשים במצב הדיווח הרציף, כמו מדי תאוצה וג'ירוסקופים, לא מקבלים אירועים.
- חיישנים שמשתמשים במצבי הדיווח on-change או one-shot לא מקבלים אירועים.
בגלל ההגבלות האלה, מומלץ לזהות אירועי חיישנים כשהאפליקציה פועלת בחזית או כחלק משירות שפועל בחזית.
ביטול הרישום של מאזינים לחיישנים
חשוב לבטל את הרישום של מאזין החיישן כשמסיימים להשתמש בחיישן או כשפעילות החיישן מושהית. אם מאזין לחיישן רשום והפעילות שלו מושהית, החיישן ימשיך לאסוף נתונים ולהשתמש במשאבי הסוללה, אלא אם תבטלו את הרישום של החיישן. הקוד הבא מראה איך להשתמש ב-method 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
כדי להעביר נתונים בין המכשיר לאמולטור:
- מוודאים שניפוי באגים ב-USB מופעל במכשיר.
- מחברים את המכשיר למחשב הפיתוח באמצעות כבל USB.
- מפעילים את האפליקציה SdkControllerSensor במכשיר.
- באפליקציה, בוחרים את החיישנים שרוצים לדמות.
מריצים את הפקודה הבאה של
adb:- מפעילים את האמולטור. עכשיו אתם אמורים להיות מסוגלים להזיז את המכשיר כדי להחיל טרנספורמציות על האמולטור.
$ adb forward tcp:1968 tcp:1968
הערה: אם התנועות שאתם מבצעים במכשיר הפיזי לא משנות את האמולטור, נסו להריץ שוב את הפקודה adb משלב 5.
מידע נוסף זמין במדריך לאמולטור Android.
אל תחסמו את השיטה onSensorChanged()
נתוני החיישנים יכולים להשתנות בקצב גבוה, ולכן יכול להיות שהמערכת תפעיל את השיטה onSensorChanged(SensorEvent) לעיתים קרובות. מומלץ לבצע כמה שפחות פעולות בתוך השיטה onSensorChanged(SensorEvent) כדי לא לחסום אותה. אם צריך לסנן או לצמצם את נתוני החיישנים באפליקציה, צריך לעשות את זה מחוץ ל-method onSensorChanged(SensorEvent).
הימנעו משימוש בשיטות או בסוגי חיישנים שהוצאו משימוש
כמה שיטות וקבועים הוצאו משימוש.
באופן ספציפי, סוג החיישן TYPE_ORIENTATION הוצא משימוש. כדי לקבל נתוני כיוון, צריך להשתמש ב-method getOrientation(). באופן דומה, סוג החיישן TYPE_TEMPERATURE הוצא משימוש. במקום זאת, צריך להשתמש בסוג החיישן TYPE_AMBIENT_TEMPERATURE במכשירים עם Android 4.0.
אימות החיישנים לפני השימוש בהם
תמיד צריך לוודא שחיישן קיים במכשיר לפני שמנסים לקבל ממנו נתונים. אל תניחו שחיישן מסוים קיים רק כי הוא נפוץ. יצרני המכשירים לא נדרשים לספק חיישנים מסוימים במכשירים שלהם.
בחרו את השהיית החיישן בקפידה
כשרושמים חיישן באמצעות השיטה registerListener(), חשוב לבחור קצב העברת נתונים שמתאים לאפליקציה או לתרחיש לדוגמה. חיישנים יכולים לספק נתונים בקצב גבוה מאוד. אם המערכת שולחת נתונים נוספים שאתם לא צריכים, זה מבזבז משאבי מערכת וצורך סוללה.