ברוב המכשירים עם Android יש חיישנים מובנים למדידת תנועה, כיוון ותנאי סביבה שונים. החיישנים האלה מסוגלים לספק נתונים גולמיים בדיוק ובדיוק, והן שימושיות אם אתם רוצים לעקוב אחרי תנועת מכשיר תלת-ממדית, או שאתם רוצים לעקוב אחרי שינויים בסביבה ברקע ליד מכשיר. לדוגמה, המשחק יכול לעקוב אחרי קריאות מחיישן הכבידה של המכשיר כדי להסיק תנועות מורכבות של המשתמש ותנועות, כמו הטיה, ניעור, סיבוב או נדנדה. באופן דומה, אפליקציית מזג אוויר עשויה להשתמש לחיישן הטמפרטורה ולחיישן הלחות במכשיר כדי לחשב את נקודת הטל ולדווח עליהן, או נסיעה האפליקציה עשויה להשתמש בחיישן השדה הגאומגנטי ובמד התאוצה כדי לדווח על מצפן בנושא.
כדאי לעיין במקורות המידע שקשורים לנושא:
פלטפורמת Android תומכת בשלוש קטגוריות רחבות של חיישנים:
- חיישני תנועה
החיישנים האלה מודדים כוחות תאוצה וכוחות סיבוב בשלושה צירים. הזה הקטגוריה כוללת מדי תאוצה, חיישני כוח כבידה, ג'ירוסקופים ווקטור סיבובי מחיישנים.
- חיישנים סביבתיים
החיישנים האלה מודדים פרמטרים סביבתיים שונים, כמו טמפרטורת האוויר בסביבה לחץ, תאורה ולחות. הקטגוריה הזו כוללת ברומטר, פוטומטר ותרמומטרים.
- חיישני מיקום
החיישנים האלה מודדים את המיקום הפיזי של המכשיר. הקטגוריה הזו כוללת חיישני כיוון ומגנטומטרים.
אפשר לגשת לחיישנים שזמינים במכשיר ולקבל נתוני חיישנים גולמיים באמצעות מכשיר Android מסגרת החיישן. מסגרת החיישן מספקת כמה כיתות וממשקים שיעזרו לכם לבצע מגוון רחב של משימות שקשורות לחיישנים. לדוגמה, אפשר להשתמש במסגרת החיישנים כדי:
- איך בודקים אילו חיישנים זמינים במכשיר.
- קביעת היכולות של חיישן ספציפי, למשל הטווח המקסימלי, היצרן וההספק דרישות ופתרון.
- איסוף נתוני חיישנים גולמיים והגדרת הקצב המינימלי שבו אתם אוספים נתוני חיישנים.
- רישום וביטול רישום של פונקציות האזנה לאירועים של חיישנים שעוקבים אחר שינויים בחיישנים.
בנושא הזה נספק סקירה כללית על החיישנים שזמינים בפלטפורמת 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 |
חומרה | מדידת מהירות הסיבוב של המכשיר ברדיאנים לשנייה סביב כל אחד משלושת הצירים הפיזיים (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
על ידי קריאה ל-method getSystemService()
והעברת הארגומנט SENSOR_SERVICE
. לדוגמה:
private lateinit var sensorManager: SensorManager ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
private SensorManager sensorManager; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
לאחר מכן, אפשר לקבל רשימה של כל החיישנים במכשיר באמצעות קריאה ל-method getSensorList()
ושימוש בערך הקבוע TYPE_ALL
. לדוגמה:
val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)
List<Sensor> deviceSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
אם רוצים לרשום את כל החיישנים מסוג נתון, אפשר להשתמש בערך קבוע אחר במקום TYPE_ALL
, כמו TYPE_GYROSCOPE
, TYPE_LINEAR_ACCELERATION
או TYPE_GRAVITY
.
אפשר גם לקבוע אם קיים חיישן מסוג מסוים במכשיר באמצעות השיטה getDefaultSensor()
וקביעת הסוג
קבוע לחיישן ספציפי. אם במכשיר יש יותר מחיישן אחד מסוג מסוים,
יש להגדיר את החיישנים כחיישן ברירת המחדל. אם לא קיים חיישן ברירת מחדל לחיישן מסוים
סוג החיישן, קריאה לשיטה מחזירה null, כלומר אין במכשיר
באמצעות חיישן. לדוגמה, הקוד הבא בודק אם יש מגנטומטר במכשיר:
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. }
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.
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 } }
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
שמוגדר בקובץ main.xml בתור sensor_data
.
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) } }
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) כדי לרשום ולבטל את הרישום של אירוע החיישן
ל-הקשיב. מומלץ תמיד להשבית חיישנים שאתם לא צריכים, במיוחד
הפעילות מושהית. אם לא עושים זאת, הסוללה עלולה לרוקן את הסוללה בעוד כמה שעות בלבד, מפני שחלק מהחיישנים
יש דרישות כוח משמעותיות שיכולות לעלות על חיי הסוללה במהירות. המערכת לא משביתה את החיישנים באופן אוטומטי כשהמסך כבוי.
טיפול בתצורות שונות של חיישנים
ב-Android אין הגדרה סטנדרטית של חיישן למכשירים. כלומר, יצרני מכשירים יכולים לשלב כל תצורת חיישן שהם רוצים מכשירי Android. כתוצאה מכך, המכשירים יכולים לכלול מגוון חיישנים במגוון רחב של הגדרות. אם האפליקציה מסתמכת על סוג מסוים של חיישן, עליך לוודא החיישן קיים במכשיר כדי שהאפליקציה תוכל לפעול.
יש שתי אפשרויות לוודא שחיישן מסוים נמצא במכשיר:
- זיהוי חיישנים בזמן הריצה והפעלה או השבתה של תכונות האפליקציה בהתאם.
- להשתמש במסננים של Google Play כדי לטרגט מכשירים עם תצורות חיישנים ספציפיות.
בסעיפים הבאים נסביר על כל אפשרות.
זיהוי חיישנים בזמן ריצה
אם האפליקציה משתמשת בסוג מסוים של חיישן אבל לא מסתמכת עליו, אפשר להשתמש מסגרת החיישן לזיהוי החיישן בזמן ריצה, ואז השבתה או הפעלה של תכונות האפליקציה לפי הצורך. לדוגמה, אפליקציית ניווט עשויה להשתמש בחיישן הטמפרטורה, חיישן לחץ, חיישן GPS וחיישן שדה גיאומגנטי להצגת הטמפרטורה, לחץ, מיקום וכיוון המצפן. אם במכשיר אין חיישן לחץ, אפשר להשתמש במסגרת החיישנים כדי לזהות את היעדר חיישן הלחץ בזמן הריצה, ואז להשבית את החלק בממשק המשתמש של האפליקציה שבו מוצג הלחץ. לדוגמה, הקוד הבא בודק אם יש חיישן לחץ במכשיר:
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. }
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. חיישני ה-IMU הבאים משתמשים במערכת הקואורדינטות הזו:

איור 1. מערכת קואורדינטות (ביחס למכשיר) שבה נעשה שימוש ב-Sensor API.
הנקודה החשובה ביותר שצריך להבין לגבי מערכת הקואורדינטות הזו היא שהצירים לא מוחלפים כשכיוון המסך של המכשיר משתנה – כלומר, מערכת הקואורדינטות של החיישן אף פעם לא משתנה כשהמכשיר זז. ההתנהגות הזו זהה להתנהגות של 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
.
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()
כדי לבטל רישום של מאזינים:
private lateinit var sensorManager: SensorManager ... override fun onPause() { super.onPause() sensorManager.unregisterListener(this) }
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
כדי להעביר נתונים בין המכשיר שלך לאמולטור, פועלים לפי השלבים הבאים שלבים:
- בדוק שUSB ניפוי הבאגים מופעל במכשיר.
- מחברים את המכשיר למכונת הפיתוח באמצעות כבל USB.
- מפעילים את האפליקציה SdkControllerSensor במכשיר.
- באפליקציה, בוחרים את החיישנים שרוצים לדמות.
מריצים את הפקודה הבאה של
adb
:- מפעילים את האמולטור. עכשיו אמורה להיות לכם אפשרות להחיל טרנספורמציות על הסימולטור על ידי הזזת המכשיר.
$ adb forward tcp:1968 tcp:1968
הערה: אם התנועה במכשיר הפיזי לא גורמת לשינוי במהלך ההדמיה, נסו להריץ שוב את הפקודה adb
משלב 5.
אפשר לקרוא מידע נוסף במאמר בנושא Android מדריך לאמולטור.
לא לחסום את השיטה onSensorChanged()
נתוני החיישנים עשויים להשתנות בקצב גבוה. כלומר, המערכת עשויה להפעיל את השיטה onSensorChanged(SensorEvent)
לעיתים קרובות. מומלץ
היא צריכה להיות מעט ככל האפשר בשיטה onSensorChanged(SensorEvent)
כדי לא לחסום אותה. אם
עליך לבצע סינון נתונים או הפחתה של נתוני החיישנים, עליך לבצע
שפועלים מחוץ ל-method onSensorChanged(SensorEvent)
.
הימנעות משימוש בשיטות או בסוגי חיישנים שהוצאו משימוש
כמה שיטות וקבועים הוצאו משימוש.
באופן ספציפי, סוג החיישן TYPE_ORIENTATION
הוצא משימוש. כדי לקבל נתוני כיוון, צריך להשתמש במקום זאת בשיטה getOrientation()
. באופן דומה,
סוג החיישן TYPE_TEMPERATURE
הוצא משימוש. צריך להשתמש
במקום זאת, סוג החיישן TYPE_AMBIENT_TEMPERATURE
במכשירים
שבהם פועלת גרסת Android 4.0.
אימות החיישנים לפני השימוש בהם
תמיד צריך לוודא שיש חיישן במכשיר לפני שמנסים לאסוף ממנו נתונים. אל תניחו שחיישן קיים רק בגלל שהוא חיישן נפוץ. יצרני המכשירים לא נדרשים לספק חיישנים ספציפיים במכשירים שלהם.
חשוב לבחור את עיכובי החיישנים בקפידה
כשרושמים חיישן בשיטת registerListener()
, חשוב לוודא שבוחרים תעריף משלוח שמתאים
או תרחיש לדוגמה. מחיישנים יכולים לספק נתונים בקצב גבוה מאוד. מתן אפשרות למערכת לשלוח
נתונים נוספים, שאינם בשימוש, מבזבזים את משאבי המערכת ומשתמשים בסוללה.