ברוב המכשירים עם Android יש חיישנים מובנים למדידת תנועה, כיוון ותנאי סביבה שונים. החיישנים האלה יכולים לספק נתונים גולמיים ברמת דיוק גבוהה, והם שימושיים אם רוצים לעקוב אחרי תנועה או מיקום תלת-ממדיים של מכשיר, או אחרי שינויים בסביבה הקרובה למכשיר. לדוגמה, משחק יכול לעקוב אחרי קריאות של חיישן הכבידה במכשיר כדי להסיק תנועות וגינונים מורכבים של המשתמש, כמו הטיה, רעד, סיבוב או תנועה קדימה ואחורה. באופן דומה, אפליקציית מזג האוויר עשויה להשתמש בחיישן הטמפרטורה ובחיישן הלחות של המכשיר כדי לחשב את נקודת הטל ולדווח עליה, או שאפליקציית נסיעות עשויה להשתמש בחיישן השדה הגיאומגנטי ובמד התאוצה כדי לדווח על כיוון מצפן.
כדאי לעיין במקורות המידע הבאים:
פלטפורמת Android תומכת בשלוש קטגוריות רחבות של חיישנים:
- חיישני תנועה
החיישנים האלה מודדים את כוחות התאוצה ואת כוחות הסיבוב בשלושה צירים. הקטגוריה הזו כוללת מדדי תאוצה, חיישני כבידה, ג'ירוסקופים וחיישנים של וקטור סיבוב.
- חיישנים סביבתיים
החיישנים האלה מודדים פרמטרים סביבתיים שונים, כמו טמפרטורה ולחץ של האוויר בסביבה, תאורה ולחות. הקטגוריה הזו כוללת ברומטר, פוטומטר ותרמומטרים.
- חיישני מיקום
החיישנים האלה מודדים את המיקום הפיזי של המכשיר. הקטגוריה הזו כוללת חיישני כיוון ומגנטומטרים.
אפשר לגשת לחיישנים שזמינים במכשיר ולקבל נתוני חיישנים גולמיים באמצעות מסגרת החיישנים של Android. מסגרת החיישן מספקת כמה כיתות וממשקים שיעזרו לכם לבצע מגוון רחב של משימות שקשורות לחיישן. לדוגמה, אפשר להשתמש במסגרת החיישנים כדי:
- איך בודקים אילו חיישנים זמינים במכשיר.
- לקבוע את היכולות של חיישן ספציפי, כמו הטווח המקסימלי, היצרן, דרישות ההספק והרזולוציה.
- איסוף נתוני חיישנים גולמיים והגדרת הקצב המינימלי שבו אתם אוספים נתוני חיישנים.
- רישום וביטול רישום של פונקציות שמאזינות לאירועים של חיישנים ומנטרות שינויים בחיישנים.
בנושא הזה נספק סקירה כללית על החיישנים שזמינים בפלטפורמת Android. בנוסף, מופיעה בו מבוא למסגרת החיישן.
מבוא לחיישנים
מסגרת החיישנים של Android מאפשרת לכם לגשת למגוון סוגים של חיישנים. חלק מהחיישנים האלה מבוססי חומרה וחלקם מבוססי תוכנה. חיישנים מבוססי-חומרה הם רכיבים פיזיים שמוטמעים במכשיר נייד או בטאבלט. הם מפיקים את הנתונים שלהם על ידי מדידה ישירה של מאפייני סביבה ספציפיים, כמו תאוצה, עוצמת השדה הגיאומגנטי או שינוי זווית. חיישנים מבוססי-תוכנה הם לא מכשירים פיזיים, אבל הם מחקים חיישנים מבוססי-חומרה. חיישנים מבוססי-תוכנה נגזרים מהנתונים של אחד או יותר מהחיישנים מבוססי-החומרה, ולפעמים נקראים חיישנים וירטואליים או חיישנים סינתטיים. חיישן התאוצה הליניארית וחישן הכבידה הם דוגמאות לחיישנים מבוססי-תוכנה. בטבלה 1 מפורט סיכום של החיישנים שנתמכים בפלטפורמת Android.
רק במכשירים מעטים עם Android יש כל סוגי החיישנים. לדוגמה, רוב הטאבלטים והטלפונים הניידים כוללים מד תאוצה ומגנטומטר, אבל פחות מכשירים כוללים ברומטר או מדחום. בנוסף, למכשיר יכול להיות יותר ממכשיר אחד מסוג נתון. לדוגמה, במכשיר יכולים להיות שני חיישני כבידה, לכל אחד מהם טווח שונה.
חיישן | סוג | תיאור | שימושים נפוצים |
---|---|---|---|
TYPE_ACCELEROMETER |
חומרה | מדידת כוח התאוצה ב-m/s2 שמופעל על מכשיר בכל שלושת הצירים הפיזיים (x, y ו-z), כולל כוח הכבידה. | זיהוי תנועה (רעידות, הטיות וכו'). |
TYPE_AMBIENT_TEMPERATURE |
חומרה | מדידת טמפרטורת החדר בסביבה במעלות צלזיוס (°C). ראו הערה בהמשך. | מעקב אחרי טמפרטורות האוויר. |
TYPE_GRAVITY |
תוכנה או חומרה | מדידת כוח הכבידה ב-m/s2 שפועל על מכשיר בכל שלושת הצירים הפיזיים (x, y, z). | זיהוי תנועה (רעידות, הטיות וכו'). |
TYPE_GYROSCOPE |
חומרה | מדידת מהירות הסיבוב של המכשיר ברדיאנים לשנייה סביב כל אחד משלושת הצירים הפיזיים (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 ברמה 14 של ה-API. |
מעקב אחרי הטמפרטורות. |
מסגרת חיישנים
אפשר לגשת לחיישנים האלה ולקבל נתוני חיישנים גולמיים באמצעות מסגרת החיישנים של Android.
מסגרת החיישן היא חלק מחבילת android.hardware
, והיא כוללת את הכיתות והממשקים הבאים:
SensorManager
- אפשר להשתמש בכיתה הזו כדי ליצור מופע של שירות החיישן. בכיתה הזו יש שיטות שונות לגישה לחיישנים וליצירת רשימה שלהם, לרישום ולביטול רישום של מאזינים לאירועים של חיישנים ולקבלת מידע על כיוון. בכיתה הזו יש גם כמה קבועים של חיישנים שמשמשים לדיווח על דיוק החיישן, להגדרת שיעורי צבירת נתונים ולכיול חיישנים.
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 מופיעה סיכום של הזמינות של כל חיישן לפי פלטפורמה. רק ארבע פלטפורמות מופיעות ברשימה כי הן הפלטפורמות שבהן בוצעו שינויים בחיישני ה-GPS. חיישנים שמפורטים כמיושנים עדיין זמינים בפלטפורמות הבאות (בתנאי שהחיישן נמצא במכשיר), בהתאם למדיניות של Android בנושא תאימות עתידית.
חיישן | 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()
כדי לקבל את הרזולוציה של חיישן ואת טווח המדידה המקסימלי שלו. אפשר גם להשתמש ב-method getPower()
כדי לקבל את דרישות החשמל של חיישן.
שתי השיטות הציבוריות שימושיות במיוחד אם רוצים לבצע אופטימיזציה של האפליקציה לחיישנים של יצרנים שונים או לגרסאות שונות של חיישן. לדוגמה, אם האפליקציה שלכם צריכה לעקוב אחרי תנועות של משתמשים כמו הטיה ורעד, תוכלו ליצור קבוצה אחת של כללי סינון נתונים ואופטימיזציות למכשירים חדשים יותר שיש בהם חיישן כבידה של ספק ספציפי, וקבוצה אחרת של כללי סינון נתונים ואופטימיזציות למכשירים שאין בהם חיישן כבידה ויש בהם רק תאוצה. בדוגמת הקוד הבאה מוסבר איך משתמשים בשיטות getVendor()
ו-getVersion()
כדי לעשות זאת. בדוגמה הזו, אנחנו מחפשים חיישן כבידה שבו Google LLC מופיעה כספק ומספר הגרסה הוא 3. אם החיישן הספציפי הזה לא נמצא במכשיר, אנחנו מנסים להשתמש בחיישן ה-accelerometer.
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()
שימושית כי היא מאפשרת לקבוע את הקצב המקסימלי שבו חיישן יכול לאסוף נתונים. אם תכונות מסוימות באפליקציה שלכם דורשות שיעורי צבירת נתונים גבוהים או חיישן סטרימינג, תוכלו להשתמש בשיטה הזו כדי לקבוע אם חיישן מסוים עומד בדרישות האלה, ולאחר מכן להפעיל או להשבית את התכונות הרלוונטיות באפליקציה בהתאם.
זהירות: קצב האיסוף המקסימלי של נתונים מהחיישן לא תמיד זהה לקצב שבו מסגרת החיישן מעבירה את נתוני החיישן לאפליקציה. מסגרת החיישנים מדווחת על נתונים באמצעות אירועי חיישנים, ומספר גורמים משפיעים על הקצב שבו האפליקציה מקבלת אירועי חיישנים. מידע נוסף זמין במאמר מעקב אחרי אירועים בחיישני תנועה.
מעקב אחרי אירועים של חיישנים
כדי לעקוב אחרי נתוני חיישן גולמיים, צריך להטמיע שתי שיטות קריאה חוזרת (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
שמוגדר בקובץ 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 מיקרו-שניות). החל מגרסה 3.0 של Android (רמה 11 של API), אפשר לציין את העיכוב גם כערך מוחלט (במיליוניות השנייה).
ההשהיה שציינתם היא רק הצעה להשהיה. מערכת Android ואפליקציות אחרות יכולות לשנות את העיכוב הזה. מומלץ לציין את העיכוב הארוך ביותר האפשרי, כי בדרך כלל המערכת משתמשת בעיכוב קצר יותר מהעיכוב שציינתם (כלומר, כדאי לבחור את קצב הדגימה האיטי ביותר שעדיין עומד בצרכים של האפליקציה). שימוש בהשהיה ארוכה יותר יוצר עומס נמוך יותר על המעבד, ולכן צורך פחות חשמל.
אין שיטה ציבורית לקביעת הקצב שבו מסגרת החיישנים שולחת אירועי חיישנים לאפליקציה. עם זאת, אפשר להשתמש בחותמות הזמן שמשויכות לכל אירוע חיישנים כדי לחשב את קצב הדגימה במספר אירועים. אין צורך לשנות את קצב הדגימה (העיכוב) אחרי שמגדירים אותו. אם מסיבה כלשהי אתם צריכים לשנות את העיכוב, תצטרכו לבטל את הרישום של מאזין החיישן ולרשום אותו מחדש.
חשוב גם לציין שבדוגמה הזו נעשה שימוש בשיטות ה-callback 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. חיישני ה-GPS הבאים משתמשים במערכת הקואורדינטות הזו:
הנקודה החשובה ביותר שצריך להבין לגבי מערכת הקואורדינטות הזו היא שהצירים לא מוחלפים כשכיוון המסך של המכשיר משתנה – כלומר, מערכת הקואורדינטות של החיישן אף פעם לא משתנה כשהמכשיר זז. ההתנהגות הזו זהה להתנהגות של מערכת הקואורדינטות של OpenGL.
חשוב גם להבין שאסור להניח שהכיוון הטבעי (ברירת המחדל) של המכשיר הוא לרוחב. הכיוון הטבעי של הרבה מכשירי טאבלט הוא לרוחב. בנוסף, מערכת הקואורדינטות של החיישן תמיד מבוססת על הכיוון הטבעי של המכשיר.
לבסוף, אם האפליקציה שלכם מתאימה את נתוני החיישן לתצוגה במסך, צריך להשתמש ב-method getRotation()
כדי לקבוע את סיבוב המסך, ואז להשתמש ב-method remapCoordinateSystem()
כדי למפות את קואורדינטות החיישן לקווי הרוחב והאורך של המסך. צריך לעשות זאת גם אם המניפסט מציין תצוגה בפורמט לאורך בלבד.
הערה: יש חיישנים ושיטות שמשתמשים במערכת קואורדינטות יחסית למערכת העזר של העולם (בניגוד למערכת העזר של המכשיר). החיישנים והשיטות האלה מחזירים נתונים שמייצגים את תנועת המכשיר או את המיקום שלו ביחס לכדור הארץ. מידע נוסף זמין בשיטה getOrientation()
, בשיטה getRotationMatrix()
, ב-Orientation Sensor וב-Rotation Vector Sensor.
הגבלת קצב של יצירת בקשות בחיישן
כדי להגן על מידע רגיש פוטנציאלי של משתמשים, אם האפליקציה שלכם מטרגטת את Android 12 (רמת API 31) ואילך, המערכת מגבילה את קצב הרענון של נתונים מסוימים ממכשירי חיישן תנועה ומכשירי חיישן מיקום. הנתונים האלה כוללים ערכים שתועדו על ידי מד התאוצה, הג'ירוסקופ וחיישן השדה הגיאומגנטי של המכשיר.
המגבלה על קצב הרענון תלויה באופן שבו אתם ניגשים לנתוני החיישנים:
- אם מפעילים את השיטה
registerListener()
כדי לעקוב אחרי אירועי חיישנים, קצב הדגימה של החיישן מוגבל ל-200 הרץ. זה נכון לכל הווריאנטים של השיטהregisterListener()
עם עומס יתר. - אם משתמשים בכיתה
SensorDirectChannel
, קצב הדגימה של החיישן מוגבל ל-RATE_NORMAL
, שבדרך כלל הוא כ-50 Hz.
אם האפליקציה שלכם צריכה לאסוף נתונים מחיישן תנועה בקצב גבוה יותר, עליכם להצהיר על ההרשאה HIGH_SAMPLING_RATE_SENSORS
, כפי שמתואר בקטע הקוד הבא. אחרת, אם האפליקציה תנסה לאסוף נתונים של חיישן תנועה בתדירות גבוהה יותר בלי להצהיר על ההרשאה הזו, תתרחש הודעת SecurityException
.
<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 Emulator
במהדורת Android Emulator יש קבוצה של אמצעי בקרה וירטואליים לחיישנים, שמאפשרים לבדוק חיישנים כמו מד תאוצה, טמפרטורה סביבתית, מגנטומטר, קרבה, אור ועוד.
הסימולטור משתמש בחיבור למכשיר 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 Emulator.
לא לחסום את השיטה onSensorChanged()
נתוני החיישנים יכולים להשתנות בתדירות גבוהה, ולכן יכול להיות שהמערכת תבצע קריאה ל-method onSensorChanged(SensorEvent)
לעיתים קרובות. מומלץ לבצע כמה שפחות פעולות בתוך השיטה onSensorChanged(SensorEvent)
כדי לא לחסום אותה. אם באפליקציה שלכם אתם צריכים לבצע סינון נתונים או הפחתה של נתוני חיישנים, עליכם לבצע את הפעולות האלה מחוץ ל-method onSensorChanged(SensorEvent)
.
הימנעות משימוש בשיטות או בסוגי חיישנים שהוצאו משימוש
כמה שיטות וקבועים הוצאו משימוש.
באופן ספציפי, סוג החיישן TYPE_ORIENTATION
הוצא משימוש. כדי לקבל נתוני כיוון, צריך להשתמש במקום זאת בשיטה getOrientation()
. כמו כן, סוג החיישן TYPE_TEMPERATURE
הוצא משימוש. במקום זאת, צריך להשתמש בסוג החיישן TYPE_AMBIENT_TEMPERATURE
במכשירים עם Android 4.0.
אימות החיישנים לפני השימוש בהם
תמיד צריך לוודא שיש חיישן במכשיר לפני שמנסים לאסוף ממנו נתונים. אל תניחו שחיישן קיים רק בגלל שהוא חיישן נפוץ. יצרני המכשירים לא נדרשים לספק חיישנים ספציפיים במכשירים שלהם.
חשוב לבחור את עיכובי החיישנים בקפידה
כשרושמים חיישן באמצעות השיטה registerListener()
, חשוב לבחור קצב העברה שמתאים לאפליקציה או לתרחיש לדוגמה. חיישנים יכולים לספק נתונים במהירויות גבוהות מאוד. אם תאפשרו למערכת לשלוח נתונים נוספים שאתם לא צריכים, תגרמו לבזבוז משאבי המערכת ולשימוש באנרגיה מהסוללה.