نمای کلی حسگرها

اکثر دستگاه‌های مجهز به Android حسگرهای داخلی دارند که حرکت، جهت، و شرایط محیطی مختلف را اندازه‌گیری می‌کنند. این حسگرها قادر به ارائه داده‌های خام با دقت و صحت بالا هستند و اگر بخواهید حرکت یا موقعیت سه‌بعدی دستگاه را پایش کنید یا بخواهید تغییرات محیط اطراف دستگاه را پایش کنید، مفید هستند. برای مثال، یک بازی ممکن است خوانش‌های حسگر گرانش دستگاه را ردیابی کند تا اشاره‌ها و حرکات پیچیده کاربر را استنباط کند، مانند کج کردن، تکان دادن، چرخاندن، یا تاب دادن. به‌طور مشابه، یک برنامه هواشناسی ممکن است از حسگر دمای دستگاه و حسگر رطوبت برای محاسبه و گزارش نقطه شبنم استفاده کند، یا یک برنامه سفر ممکن است از حسگر میدان ژئومغناطیسی و شتاب‌سنج برای گزارش جهت قطب‌نما استفاده کند.

پلاتفرم Android از سه دسته کلی حسگر پشتیبانی می‌کند:

  • حسگرهای حرکتی

    این حسگرها نیروهای شتاب و نیروهای چرخشی را در سه محور اندازه‌گیری می‌کنند. این دسته شامل شتاب‌سنج‌ها، حسگرهای گرانش، ژیروسکوپ‌ها، و حسگرهای بردار چرخشی است.

  • حسگرهای محیطی

    این حسگرها پارامترهای محیطی مختلفی را اندازه‌گیری می‌کنند، مانند دما و فشار هوای محیط، روشنایی، و رطوبت. این دسته شامل فشارسنج، نورسنج، و دماسنج می‌شود.

  • حسگرهای موقعیت

    این حسگرها موقعیت فیزیکی دستگاه را اندازه‌گیری می‌کنند. این دسته شامل حسگرهای جهت‌یابی و مغناطیس‌سنج‌ها می‌شود.

بااستفاده از چارچوب حسگر Android می‌توانید به حسگرهای موجود در دستگاه دسترسی پیدا کنید و داده‌های خام حسگر را دریافت کنید. چارچوب حسگر چندین کلاس و میانای مختلف ارائه می‌دهد که به شما کمک می‌کند طیف گسترده‌ای از کارهای مرتبط با حسگر را انجام دهید. برای مثال، می‌توانید از چارچوب حسگر برای انجام کارهای زیر استفاده کنید:

  • مشخص کردن اینکه کدام حسگرها در دستگاه دردسترس است.
  • قابلیت‌های یک حسگر خاص را تعیین کنید، مانند حداکثر برد، سازنده، الزامات برق، و وضوح آن.
  • داده‌های خام حسگر را دریافت کنید و حداقل نرخ دریافت داده‌های حسگر را تعریف کنید.
  • ثبت و لغو ثبت شنوندگان رویداد حسگر که تغییرات حسگر را پایش می‌کنند.

این موضوع نمای کلی از حسگرهای دردسترس در پلاتفرم Android ارائه می‌دهد. همچنین مقدمه‌ای بر چارچوب حسگر ارائه می‌دهد.

مقدمه‌ای بر حسگرها

چارچوب حسگر Android به شما امکان می‌دهد به انواع مختلفی از حسگرها دسترسی داشته باشید. برخی‌از این حسگرها سخت‌افزارپایه و برخی نرم‌افزارپایه هستند. حسگرهای مبتنی بر سخت‌افزار اجزای فیزیکی هستند که در دستگاه تلفن یا رایانه لوحی تعبیه شده‌اند. این حسگرها داده‌هایشان را با اندازه‌گیری مستقیم ویژگی‌های محیطی خاص، مانند شتاب، قدرت میدان ژئومغناطیسی، یا تغییر زاویه‌ای، به‌دست می‌آورند. حسگرهای نرم‌افزارمحور دستگاه‌های فیزیکی نیستند، هرچند از حسگرهای سخت‌افزارمحور تقلید می‌کنند. حسگرهای نرم‌افزاری داده‌هایشان را از یک یا چند حسگر سخت‌افزاری می‌گیرند و گاهی حسگرهای مجازی یا حسگرهای مصنوعی نامیده می‌شوند. حسگر شتاب خطی و حسگر گرانش نمونه‌هایی از حسگرهای نرم‌افزارمحور هستند. جدول ۱ حسگرهایی را که پلاتفرم Android از آن‌ها پشتیبانی می‌کند خلاصه می‌کند.

تعداد کمی از دستگاه‌های مجهز به 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 سخت‌افزار سطح نور محیط (روشنایی) را برحسب لوکس اندازه‌گیری می‌کند. درحال کنترل روشنایی صفحه‌نمایش.
TYPE_LINEAR_ACCELERATION نرم‌افزار یا سخت‌افزار نیروی شتاب را برحسب متر بر مجذور ثانیه (m/s2) که بر هر سه محور فیزیکی (x،‏ y، و z) دستگاه اعمال می‌شود، بدون احتساب نیروی گرانش، اندازه‌گیری می‌کند. پایش شتاب در امتداد یک محور.
TYPE_MAGNETIC_FIELD سخت‌افزار میدان ژئومغناطیسی محیط را برای هر سه محور فیزیکی (x،‏ y،‏ z) برحسب میکروتسلا (μT) اندازه‌گیری می‌کند. درحال ایجاد قطب‌نما.
TYPE_ORIENTATION نرم‌افزار درجه چرخشی را که دستگاه حول هر سه محور فیزیکی (x،‏ y،‏ z) انجام می‌دهد اندازه‌گیری می‌کند. از سطح ای‌پی‌آی ۳ می‌توانید ماتریس شیب و ماتریس چرخش را برای دستگاه بااستفاده از حسگر گرانش و حسگر میدان ژئومغناطیسی در ارتباط با روش getRotationMatrix() به‌دست آورید. درحال تعیین موقعیت دستگاه.
TYPE_PRESSURE سخت‌افزار فشار هوای محیط را برحسب هکتوپاسکال یا میلی‌بار اندازه‌گیری می‌کند. پایش تغییرات فشار هوا.
TYPE_PROXIMITY سخت‌افزار مجاورت شیء را برحسب سانتی‌متر نسبت به صفحه‌نمایش دستگاه اندازه‌گیری می‌کند. این حسگر معمولاً برای تعیین اینکه آیا گوشی نزدیک گوش فردی قرار دارد یا نه استفاده می‌شود. موقعیت تلفن درطول تماس.
TYPE_RELATIVE_HUMIDITY سخت‌افزار رطوبت نسبی محیط را برحسب درصد (%) اندازه‌گیری می‌کند. پایش نقطه شبنم، رطوبت مطلق، و رطوبت نسبی.
TYPE_ROTATION_VECTOR نرم‌افزار یا سخت‌افزار جهت دستگاه را با ارائه سه عنصر بردار چرخش دستگاه اندازه‌گیری می‌کند. تشخیص حرکت و تشخیص چرخش.
TYPE_TEMPERATURE سخت‌افزار دمای دستگاه را به درجه سلسیوس (°C) اندازه‌گیری می‌کند. پیاده‌سازی این حسگر در دستگاه‌های مختلف متفاوت است و این حسگر در سطح میانای برنامه‌سازی کاربردی ۱۴ با حسگر TYPE_AMBIENT_TEMPERATURE جایگزین شده است درحال پایش دما.

چارچوب حسگر

بااستفاده از چارچوب حسگر Android می‌توانید به این حسگرها دسترسی پیدا کنید و داده‌های خام حسگر را به‌دست آورید. چارچوب حسگر بخشی از بسته android.hardware است و شامل کلاس‌ها و میاناهای زیر می‌شود:

SensorManager
می‌توانید از این کلاس برای ایجاد نمونه‌ای از سرویس حسگر استفاده کنید. این کلاس روش‌های مختلفی برای دسترسی و فهرست کردن حسگرها، ثبت و لغو ثبت شنوندگان رویداد حسگر، و کسب اطلاعات جهت‌یابی ارائه می‌دهد. این کلاس همچنین چندین ثابت حسگر ارائه می‌دهد که برای گزارش دقت حسگر، تنظیم نرخ‌های دریافت داده، و کالیبره کردن حسگرها استفاده می‌شود.
Sensor
می‌توانید از این کلاس برای ایجاد نمونه‌ای از یک حسگر خاص استفاده کنید. این کلاس روش‌های مختلفی را ارائه می‌دهد که به شما امکان می‌دهد قابلیت‌های حسگر را تعیین کنید.
SensorEvent
سیستم از این کلاس برای ایجاد شیء رویداد حسگر استفاده می‌کند که اطلاعاتی درباره رویداد حسگر ارائه می‌دهد. شیء رویداد حسگر شامل اطلاعات زیر است: داده‌های خام حسگر، نوع حسگری که رویداد را تولید کرده است، دقت داده‌ها، و مُهر زمان برای رویداد.
SensorEventListener
می‌توانید از این میانای کاربری برای ایجاد دو روش بازخوانی استفاده کنید که وقتی مقادیر حسگر یا دقت حسگر تغییر می‌کند، اعلان (رویدادهای حسگر) دریافت می‌کنند.

در یک برنامه معمولی، از این APIهای مرتبط با حسگر برای انجام دو وظیفه اصلی استفاده می‌کنید:

  • شناسایی حسگرها و قابلیت‌های حسگر

    شناسایی حسگرها و قابلیت‌های حسگر در زمان اجرا مفید است اگر برنامه شما دارای ویژگی‌هایی باشد که به انواع یا قابلیت‌های خاص حسگر وابسته هستند. برای مثال، ممکن است بخواهید همه حسگرهای موجود در دستگاه را شناسایی کنید و هر ویژگی برنامه‌ای را که به حسگرهای موجود وابسته است غیرفعال کنید. به‌همین ترتیب، ممکن است بخواهید همه حسگرهای نوع خاصی را شناسایی کنید تا بتوانید پیاده‌سازی حسگری را انتخاب کنید که عملکرد بهینه برای برنامه‌تان داشته باشد.

  • نظارت بر رویدادهای حسگر

    پایش رویدادهای حسگر روشی است که ازطریق آن داده‌های خام حسگر را به‌دست می‌آورید. هر بار که حسگر تغییری در پارامترهایی که اندازه‌گیری می‌کند تشخیص دهد، رویداد حسگر رخ می‌دهد. رویداد حسگر چهار بخش اطلاعاتی به شما ارائه می‌دهد: نام حسگری که رویداد را راه‌اندازی کرده است، مُهر زمان رویداد، دقت رویداد، و داده‌های خام حسگر که رویداد را راه‌اندازی کرده است.

دردسترس بودن حسگر

درحالی‌که دردسترس بودن حسگر از دستگاهی به دستگاه دیگر متفاوت است، می‌تواند بین نسخه‌های Android نیز متفاوت باشد. دلیل این امر این است که حسگرهای Android درطول چندین نسخه پلاتفرم معرفی شده‌اند. برای مثال، حسگرهای زیادی در Android 1.5 (سطح میانای برنامه‌سازی کاربردی ۳) معرفی شدند، اما برخی‌از آن‌ها تا Android 2.3 (سطح میانای برنامه‌سازی کاربردی ۹) پیاده‌سازی نشدند و برای استفاده دردسترس نبودند. به‌همین ترتیب، چند حسگر در Android 2.3 (سطح میانای برنامه‌سازی کاربردی ۹) و Android 4.0 (سطح میانای برنامه‌سازی کاربردی ۱۴) معرفی شدند. دو حسگر منسوخ شده‌اند و حسگرهای جدیدتر و بهتری جایگزین آن‌ها شده‌اند.

جدول ۲ دردسترس بودن هر حسگر را به‌صورت پلاتفرم به پلاتفرم خلاصه می‌کند. فقط چهار پلاتفرم فهرست شده است زیرا این پلاتفرم‌ها شامل تغییرات حسگر هستند. حسگرهایی که به‌عنوان منسوخ‌شده فهرست شده‌اند، همچنان در پلاتفرم‌های بعدی دردسترس هستند (به‌شرطی که حسگر در دستگاه وجود داشته باشد)، که این امر با خط‌مشی سازگاری روبه‌جلوی Android مطابقت دارد.

جدول ۲. دردسترس بودن حسگر براساس پلاتفرم.

حسگر Android 4.0
(سطح میانای برنامه‌سازی کاربردی ۱۴)
Android 2.3
(API Level 9)
Android 2.2
(API Level 8)
Android 1.5
(سطح میانای برنامه‌سازی کاربردی ۳)
TYPE_ACCELEROMETER بله بله بله بله
TYPE_AMBIENT_TEMPERATURE بله نامربوط نامربوط نامربوط
TYPE_GRAVITY بله بله نامربوط نامربوط
TYPE_GYROSCOPE بله بله نامشخص۱ نامشخص۱
TYPE_LIGHT بله بله بله بله
TYPE_LINEAR_ACCELERATION بله بله نامربوط نامربوط
TYPE_MAGNETIC_FIELD بله بله بله بله
TYPE_ORIENTATION بله۲ بله۲ بله۲ بله
TYPE_PRESSURE بله بله نامشخص۱ نامشخص۱
TYPE_PROXIMITY بله بله بله بله
TYPE_RELATIVE_HUMIDITY بله نامربوط نامربوط نامربوط
TYPE_ROTATION_VECTOR بله بله نامربوط نامربوط
TYPE_TEMPERATURE بله۲ بله بله بله

۱ این نوع حسگر در Android 1.5 (سطح میانای برنامه‌سازی کاربردی ۳) اضافه شد، اما تا Android 2.3 (سطح میانای برنامه‌سازی کاربردی ۹) برای استفاده دردسترس نبود.

۲ این حسگر دردسترس است، اما منسوخ شده است.

شناسایی حسگرها و قابلیت‌های حسگر

چارچوب حسگر Android چندین روش ارائه می‌دهد که به شما کمک می‌کند به‌راحتی در زمان اجرا تشخیص دهید کدام حسگرها در دستگاه وجود دارند. این میانای برنامه‌سازی کاربردی همچنین روش‌هایی را ارائه می‌دهد که به شما امکان می‌دهد قابلیت‌های هر حسگر را تعیین کنید، مانند حداکثر برد، وضوح، و نیازهای توان آن.

برای شناسایی حسگرهای موجود در دستگاه، ابتدا باید مرجعی برای سرویس حسگر دریافت کنید. برای انجام این کار، با فراخوانی روش getSystemService() و ارسال آرگومان SENSOR_SERVICE، نمونه‌ای از کلاس SensorManager ایجاد می‌کنید. برای مثال:

کاتلین

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

جاوا

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

سپس، می‌توانید با فراخوانی روش 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() برای به‌دست آوردن وضوح و حداکثر محدوده اندازه‌گیری حسگر استفاده کنید. همچنین می‌توانید از روش getPower() برای به‌دست آوردن الزامات توان حسگر استفاده کنید.

دو روش عمومی به‌ویژه زمانی مفید هستند که بخواهید برنامه‌تان را برای حسگرهای سازندگان مختلف یا نسخه‌های مختلف یک حسگر بهینه‌سازی کنید. برای مثال، اگر برنامه‌تان باید اشاره‌های کاربر مثل کج کردن و تکان دادن را پایش کند، می‌توانید یک مجموعه قانون فیلتر کردن داده و بهینه‌سازی برای دستگاه‌های جدیدتری که حسگر گرانش فروشنده خاصی را دارند ایجاد کنید، و مجموعه دیگری از قوانین فیلتر کردن داده و بهینه‌سازی برای دستگاه‌هایی که حسگر گرانش ندارند و فقط شتاب‌سنج دارند ایجاد کنید. نمونه کد زیر نشان می‌دهد که چگونه می‌توانید از روش‌های getVendor() و getVersion() برای انجام این کار استفاده کنید. در این نمونه، به‌دنبال حسگر گرانشی هستیم که Google LLC را به‌عنوان فروشنده فهرست می‌کند و شماره نسخه آن ۳ است. اگر آن حسگر خاص در دستگاه وجود نداشته باشد، سعی می‌کنیم از شتاب‌سنج استفاده کنیم.

کاتلین

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.
    }
}

روش مفید دیگر روش getMinDelay() است، که حداقل فاصله زمانی (به میکروثانیه) را که حسگر می‌تواند برای حس کردن داده‌ها استفاده کند برمی‌گرداند. هر حسگری که مقدار غیرصفر برای روش getMinDelay() برگرداند، حسگر جاری‌سازی است. حسگرهای جاری‌سازی داده‌ها را در فواصل زمانی منظم حس می‌کنند و در Android 2.3 (سطح میانای برنامه‌سازی کاربردی ۹) معرفی شدند. اگر حسگری هنگام فراخوانی روش getMinDelay() مقدار صفر را برگرداند، به این معنی است که حسگر از نوع حسگر جاری‌سازی نیست زیرا داده‌ها را فقط زمانی گزارش می‌کند که تغییری در پارامترهایی که حس می‌کند ایجاد شود.

روش getMinDelay() مفید است زیرا به شما امکان می‌دهد حداکثر نرخی را که حسگر می‌تواند داده‌ها را دریافت کند تعیین کنید. اگر برخی‌از ویژگی‌های برنامه‌تان به نرخ بالای دریافت داده یا حسگر جاری‌سازی نیاز دارد، می‌توانید از این روش استفاده کنید تا تعیین کنید آیا حسگر این الزامات را برآورده می‌کند یا نه و سپس ویژگی‌های مربوطه را در برنامه‌تان براساس آن فعال یا غیرفعال کنید.

احتیاط: حداکثر نرخ دریافت داده حسگر لزوماً نرخی نیست که چارچوب حسگر داده‌های حسگر را به برنامه شما ارائه می‌دهد. چارچوب حسگر داده‌ها را ازطریق رویدادهای حسگر گزارش می‌دهد و عوامل متعددی بر نرخی که برنامه شما رویدادهای حسگر را دریافت می‌کند تأثیر می‌گذارند. برای اطلاعات بیشتر، پایش رویدادهای حسگر را ببینید.

پایش رویدادهای حسگر

برای نظارت بر داده‌های حسگر خام، باید دو روش فراخوانی را که ازطریق میانای SensorEventListener ارائه می‌شوند پیاده‌سازی کنید: onAccuracyChanged() و onSensorChanged(). سیستم Android این روش‌ها را هرزمان که موارد زیر رخ دهد فراخوانی می‌کند:

  • دقت حسگر تغییر کند.

    در این حالت، سیستم روش onAccuracyChanged() را فراخوانی می‌کند و مرجعی به شیء Sensor که تغییر کرده است و دقت جدید حسگر را به شما ارائه می‌دهد. دقت با یکی از چهار ثابت وضعیت نشان داده می‌شود: SENSOR_STATUS_ACCURACY_LOW، SENSOR_STATUS_ACCURACY_MEDIUM، SENSOR_STATUS_ACCURACY_HIGH، یا SENSOR_STATUS_UNRELIABLE.

  • حسگر مقدار جدیدی را گزارش کند.

    در این حالت، سیستم روش onSensorChanged() را فراخوانی می‌کند و شیء SensorEvent را دراختیارتان قرار می‌دهد. یک شیء SensorEvent حاوی اطلاعاتی درباره داده‌های حسگر جدید است، ازجمله: دقت داده‌ها، حسگری که داده‌ها را تولید کرده است، مُهر زمانی که داده‌ها در آن تولید شده‌اند، و داده‌های جدیدی که حسگر ثبت کرده است.

کد زیر نحوه استفاده از روش onSensorChanged() را برای نظارت بر داده‌های حسگر نور نشان می‌دهد. این نمونه داده‌های حسگر خام را در TextView نمایش می‌دهد که در فایل main.xml به‌عنوان sensor_data تعریف شده است.

کاتلین

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() مشخص می‌شود. تأخیر داده (یا نرخ نمونه‌برداری) فاصله زمانی ارسال رویدادهای حسگر به برنامه شما را ازطریق روش فراخوانی برگشتی onSensorChanged() کنترل می‌کند. تأخیر داده پیش‌فرض برای نظارت بر تغییرات جهت صفحه معمولی مناسب است و از تأخیر ۲۰۰٬۰۰۰ میکروثانیه‌ای استفاده می‌کند. می‌توانید تأخیرهای داده دیگری را مشخص کنید، مثل SENSOR_DELAY_GAME (۲۰٬۰۰۰ میکروثانیه تأخیر)، SENSOR_DELAY_UI (۶۰٬۰۰۰ میکروثانیه تأخیر)، یا SENSOR_DELAY_FASTEST (۰ میکروثانیه تأخیر). از Android نسخه ۳.۰ (سطح میانای برنامه‌سازی کاربردی ۱۱) به بعد، می‌توانید تأخیر را به‌عنوان مقدار مطلق (به میکروثانیه) نیز مشخص کنید.

تأخیری که مشخص می‌کنید فقط تأخیر پیشنهادی است. سیستم Android و برنامه‌های دیگر می‌توانند این تأخیر را تغییر دهند. به‌عنوان یک روال مطلوب، باید بیشترین تأخیری را که می‌توانید مشخص کنید زیرا سیستم معمولاً از تأخیر کمتری نسبت‌به آنچه شما مشخص می‌کنید استفاده می‌کند (یعنی باید کندترین نرخ نمونه‌برداری را انتخاب کنید که همچنان نیازهای برنامه شما را برآورده می‌کند). استفاده از تأخیر بیشتر، بار کمتری بر پردازنده تحمیل می‌کند و بنابراین انرژی کمتری مصرف می‌کند.

هیچ روش عمومی‌ای برای تعیین نرخی که چارچوب حسگر رویدادهای حسگر را به برنامه شما ارسال می‌کند وجود ندارد؛ بااین‌حال، می‌توانید از مُهرهای زمان مرتبط با هر رویداد حسگر برای محاسبه نرخ نمونه‌برداری در چندین رویداد استفاده کنید. پس‌از تنظیم نرخ نمونه‌برداری (تأخیر)، نباید آن را تغییر دهید. اگر به هر دلیلی نیاز به تغییر تأخیر دارید، باید شنونده حسگر را لغو ثبت‌نام و دوباره ثبت‌نام کنید.

همچنین مهم است توجه داشته باشید که این مثال از روش‌های فراخوانی برگشتی onResume() و onPause() برای ثبت و لغو ثبت شنونده رویداد حسگر استفاده می‌کند. به‌عنوان بهترین روش، همیشه باید حسگرهایی را که نیاز ندارید غیرفعال کنید، به‌ویژه زمانی که فعالیتتان موقتاً متوقف شده است. اگر این کار را نکنید، ممکن است شارژ باتری ظرف چند ساعت خالی شود زیرا برخی‌از حسگرها به انرژی زیادی نیاز دارند و می‌توانند شارژ باتری را به‌سرعت مصرف کنند. وقتی صفحه‌نمایش خاموش می‌شود، سیستم حسگرها را به‌طور خودکار غیرفعال نمی‌کند.

مدیریت پیکربندی‌های مختلف حسگر

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" تنظیم کنید. این کار کمک می‌کند مطمئن شوید دستگاه‌ها می‌توانند برنامه‌تان را نصب کنند حتی اگر آن حسگر خاص را نداشته باشند. این همچنین یک روال مطلوب مدیریت پروژه است که به شما کمک می‌کند ویژگی‌هایی را که برنامه‌تان استفاده می‌کند پیگیری کنید. به‌خاطر داشته باشید که اگر برنامه شما از حسگر خاصی استفاده می‌کند اما همچنان بدون آن حسگر اجرا می‌شود، باید حسگر را در زمان اجرا شناسایی کنید و ویژگی‌های برنامه را به‌طور مناسب غیرفعال یا فعال کنید.

سیستم مختصات حسگر

به‌طور کلی، چارچوب حسگر از سیستم مختصات ۳ محوری استاندارد برای بیان مقادیر داده استفاده می‌کند. برای اکثر حسگرها، سیستم مختصات نسبت به صفحه‌نمایش دستگاه زمانی که دستگاه در جهت پیش‌فرض خود نگه داشته می‌شود، تعریف می‌شود (شکل ۱ را ببینید). وقتی دستگاه در جهت پیش‌فرض خود نگه داشته می‌شود، محور X افقی است و به سمت راست اشاره می‌کند، محور Y عمودی است و به سمت بالا اشاره می‌کند، و محور Z به سمت بیرون صفحه نمایش اشاره می‌کند. در این سیستم، مختصات پشت صفحه‌نمایش مقادیر Z منفی دارند. این سیستم مختصات توسط حسگرهای زیر استفاده می‌شود:

شکل ۱. سیستم مختصات (نسبت به دستگاه) که توسط Sensor API استفاده می‌شود.

مهم‌ترین نکته‌ای که باید درباره این سیستم مختصات بدانید این است که وقتی جهت صفحه‌نمایش دستگاه تغییر می‌کند، محورها جابه‌جا نمی‌شوند؛ یعنی سیستم مختصات حسگر با حرکت دستگاه هرگز تغییر نمی‌کند. این رفتار مشابه رفتار سیستم مختصات OpenGL است.

نکته دیگری که باید درک شود این است که برنامه شما نباید فرض کند که جهت طبیعی (پیش‌فرض) دستگاه، عمودی است. جهت طبیعی برای بسیاری از دستگاه‌های رایانه لوحی، افقی است. و سیستم مختصات حسگر همیشه براساس جهت طبیعی دستگاه است.

درنهایت، اگر برنامه‌تان داده‌های حسگر را با نمایشگر روی صفحه مطابقت می‌دهد، باید از روش getRotation() برای تعیین چرخش صفحه‌نمایش استفاده کنید و سپس از روش remapCoordinateSystem() برای نگاشت مختصات حسگر به مختصات صفحه‌نمایش استفاده کنید. حتی اگر مانیفست شما نمایش فقط عمودی را مشخص کرده باشد، باید این کار را انجام دهید.

توجه: برخی‌از حسگرها و روش‌ها از سیستم مختصاتی استفاده می‌کنند که نسبت‌به چارچوب مرجع جهان است (برخلاف چارچوب مرجع دستگاه). این حسگرها و روش‌ها داده‌هایی را برمی‌گردانند که نشان‌دهنده حرکت دستگاه یا موقعیت دستگاه نسبت به زمین است. برای اطلاعات بیشتر، روش getOrientation()، روش getRotationMatrix()، حسگر جهت، و حسگر بردار چرخش را ببینید.

محدود کردن نرخ حسگر

برای محافظت از اطلاعات بالقوه حساس درباره کاربران، اگر برنامه شما Android 12 (سطح API‏ ۳۱) یا بالاتر را هدف قرار دهد، سیستم محدودیتی برای نرخ بازآوری داده‌ها از حسگرهای حرکتی و حسگرهای موقعیت خاص اعمال می‌کند. این داده‌ها شامل مقادیر ثبت‌شده توسط شتاب‌سنج، ژیروسکوپ، و حسگر میدان ژئومغناطیسی دستگاه است.

محدودیت نرخ بازآوری به نحوه دسترسی شما به داده‌های حسگر بستگی دارد:

  • اگر روش registerListener() را برای پایش رویدادهای حسگر فراخوانی کنید، نرخ نمونه‌برداری حسگر به ۲۰۰ هرتز محدود می‌شود. این موضوع برای همه انواع سربارگذاری‌شده روش registerListener() صادق است.
  • اگر از کلاس SensorDirectChannel استفاده می‌کنید، نرخ نمونه‌برداری حسگر به RATE_NORMAL محدود می‌شود، که معمولاً حدود ۵۰ هرتز است.

اگر برنامه‌تان نیاز دارد داده‌های حسگر حرکت را با نرخ بالاتری جمع‌آوری کند، باید اجازه HIGH_SAMPLING_RATE_SENSORS را اعلام کنید، همان‌طور که در تکه‌کد زیر نشان داده شده است. درغیراین‌صورت، اگر برنامه‌تان تلاش کند داده‌های حسگر حرکت را با نرخ بالاتری بدون اعلام این اجازه جمع‌آوری کند، SecurityException رخ می‌دهد.

AndroidManifest.xml

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

روال‌های مطلوب برای دسترسی به حسگرها و استفاده از آن‌ها

هنگام طراحی پیاده‌سازی حسگر، حتماً از دستورالعمل‌هایی که در این بخش بحث شده است پیروی کنید. این دستورالعمل‌ها روال‌های مطلوب توصیه‌شده برای هر فردی است که از چارچوب حسگر برای دسترسی به حسگرها و دریافت داده‌های حسگر استفاده می‌کند.

فقط در پیش‌زمینه داده‌های حسگر را جمع‌آوری کنید

در دستگاه‌های دارای Android نسخه ۹ (سطح میانای برنامه‌سازی کاربردی ۲۸) یا بالاتر، برنامه‌هایی که در پس‌زمینه اجرا می‌شوند محدودیت‌های زیر را دارند:

  • حسگرهایی که از حالت گزارش‌دهی پیوسته استفاده می‌کنند، مثل شتاب‌سنج‌ها و ژیروسکوپ‌ها، رویدادها را دریافت نمی‌کنند.
  • حسگرهایی که از حالت‌های گزارش‌دهی 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» شامل مجموعه‌ای از کنترل‌های حسگر مجازی است که به شما امکان می‌دهد حسگرهایی مانند شتاب‌سنج، دمای محیط، مغناطیس‌سنج، مجاورت، نور، و غیره را آزمایش کنید.

شبیه‌ساز از اتصال با دستگاه Android استفاده می‌کند که برنامه SdkControllerSensor را اجرا می‌کند. توجه داشته باشید که این برنامه فقط در دستگاه‌های دارای Android 4.0 (سطح میانای برنامه‌سازی کاربردی ۱۴) یا بالاتر دردسترس است. (اگر دستگاه Android نسخه ۴.۰ دارد، باید «بازبینی ۲» نصب شده باشد.) برنامه 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 را از مرحله ۵ دوباره اجرا کنید.

برای اطلاعات بیشتر، راهنمای شبیه‌ساز Android را ببینید.

روش onSensorChanged()‎ را مسدود نکنید

داده‌های حسگر می‌توانند با نرخ بالایی تغییر کنند، به این معنی که سیستم ممکن است روش onSensorChanged(SensorEvent) را به‌طور مکرر فراخوانی کند. به‌عنوان روالی مطلوب، باید تا حد امکان کارهای کمی در روش onSensorChanged(SensorEvent) انجام دهید تا آن را مسدود نکنید. اگر برنامه شما نیاز دارد که فیلتر کردن داده‌ها یا کاهش داده‌های حسگر را انجام دهید، باید این کار را خارج از روش onSensorChanged(SensorEvent) انجام دهید.

از استفاده از روش‌ها یا انواع حسگرهای منسوخ‌شده خودداری کنید

چندین روش و ثابت منسوخ شده‌اند. به‌ویژه، نوع حسگر TYPE_ORIENTATION منسوخ شده است. برای دریافت داده‌های جهت‌یابی باید از روش getOrientation() استفاده کنید. به‌همین ترتیب، نوع حسگر TYPE_TEMPERATURE منسوخ شده است. در دستگاه‌هایی که Android نسخه ۴.۰ دارند، باید از نوع حسگر TYPE_AMBIENT_TEMPERATURE استفاده کنید.

درستی‌سنجی حسگرها قبل‌از استفاده

همیشه قبل‌از تلاش برای دریافت داده از حسگر، وجود آن را در دستگاه تأیید کنید. فرض نکنید که حسگری وجود دارد صرفاً به این دلیل که حسگری است که مکرراً استفاده می‌شود. سازندگان دستگاه ملزم نیستند حسگرهای خاصی را در دستگاه‌هایشان ارائه دهند.

تأخیر حسگر را بادقت انتخاب کنید

وقتی حسگری را با روش registerListener() ثبت می‌کنید، حتماً نرخ تحویلی را انتخاب کنید که برای برنامه یا مورد استفاده شما مناسب باشد. حسگرها می‌توانند داده‌ها را با نرخ بسیار بالایی ارائه دهند. اجازه دادن به سیستم برای ارسال داده‌های اضافی که به آن‌ها نیاز ندارید، منابع سیستم را هدر می‌دهد و شارژ باتری را مصرف می‌کند.