Hầu hết các thiết bị chạy Android đều có cảm biến tích hợp để đo chuyển động, hướng và nhiều điều kiện môi trường. Các cảm biến này có thể cung cấp dữ liệu thô với độ chính xác cao và hữu ích nếu bạn muốn theo dõi chuyển động hoặc vị trí của thiết bị 3 chiều, hoặc muốn theo dõi những thay đổi trong môi trường xung quanh gần một thiết bị. Ví dụ: một trò chơi có thể theo dõi các chỉ số từ cảm biến trọng lực của thiết bị để suy ra các cử chỉ và chuyển động phức tạp của người dùng, chẳng hạn như nghiêng, lắc, xoay hoặc vung. Tương tự, một ứng dụng thời tiết có thể sử dụng cảm biến nhiệt độ và cảm biến độ ẩm của thiết bị để tính toán và báo cáo điểm sương, hoặc một ứng dụng du lịch có thể sử dụng cảm biến từ trường và cảm biến gia tốc để báo cáo phương vị la bàn.
Hãy tham khảo các tài nguyên liên quan sau:
Nền tảng Android hỗ trợ 3 danh mục cảm biến chính:
- Cảm biến chuyển động
Các cảm biến này đo lực gia tốc và lực quay dọc theo 3 trục. Danh mục này bao gồm gia tốc kế, cảm biến trọng lực, con quay hồi chuyển và cảm biến vectơ xoay.
- Cảm biến môi trường
Các cảm biến này đo lường nhiều thông số môi trường, chẳng hạn như nhiệt độ và áp suất không khí xung quanh, độ chiếu sáng và độ ẩm. Danh mục này bao gồm khí áp kế, quang kế và nhiệt kế.
- Cảm biến vị trí
Các cảm biến này đo vị trí thực của thiết bị. Danh mục này bao gồm các cảm biến định hướng và từ kế.
Bạn có thể truy cập vào các cảm biến có trên thiết bị và thu thập dữ liệu cảm biến thô bằng cách sử dụng khung cảm biến Android. Khung cảm biến cung cấp một số lớp và giao diện giúp bạn thực hiện nhiều loại tác vụ liên quan đến cảm biến. Ví dụ: bạn có thể sử dụng khung cảm biến để làm những việc sau:
- Xác định những cảm biến có trên thiết bị.
- Xác định các chức năng của từng cảm biến, chẳng hạn như phạm vi tối đa, nhà sản xuất, yêu cầu về nguồn điện và độ phân giải.
- Thu thập dữ liệu cảm biến thô và xác định tốc độ tối thiểu mà bạn thu thập dữ liệu cảm biến.
- Đăng ký và huỷ đăng ký trình nghe sự kiện cảm biến để theo dõi các thay đổi của cảm biến.
Chủ đề này cung cấp thông tin tổng quan về các cảm biến có trên nền tảng Android. Ngoài ra, khoá học này cũng giới thiệu về khung cảm biến.
Giới thiệu về cảm biến
Khung cảm biến Android cho phép bạn truy cập vào nhiều loại cảm biến. Một số cảm biến trong số này dựa trên phần cứng và một số dựa trên phần mềm. Cảm biến dựa trên phần cứng là các thành phần vật lý được tích hợp vào thiết bị cầm tay hoặc máy tính bảng. Các cảm biến này lấy dữ liệu bằng cách đo trực tiếp các thuộc tính cụ thể của môi trường, chẳng hạn như gia tốc, cường độ từ trường địa từ hoặc sự thay đổi về góc. Cảm biến dựa trên phần mềm không phải là thiết bị thực, mặc dù chúng mô phỏng cảm biến dựa trên phần cứng. Các cảm biến dựa trên phần mềm lấy dữ liệu từ một hoặc nhiều cảm biến dựa trên phần cứng và đôi khi được gọi là cảm biến ảo hoặc cảm biến tổng hợp. Cảm biến gia tốc tuyến tính và cảm biến trọng lực là ví dụ về cảm biến dựa trên phần mềm. Bảng 1 tóm tắt các cảm biến mà nền tảng Android hỗ trợ.
Rất ít thiết bị chạy Android có mọi loại cảm biến. Ví dụ: hầu hết các thiết bị cầm tay và máy tính bảng đều có gia tốc kế và từ kế, nhưng ít thiết bị có khí áp kế hoặc nhiệt kế. Ngoài ra, một thiết bị có thể có nhiều cảm biến thuộc một loại nhất định. Ví dụ: một thiết bị có thể có 2 cảm biến trọng lực, mỗi cảm biến có một phạm vi khác nhau.
Bảng 1. Các loại cảm biến mà nền tảng Android hỗ trợ.
Cảm biến | Loại | Mô tả | Các cách dùng phổ biến |
---|---|---|---|
TYPE_ACCELEROMETER |
Phần cứng | Đo lực gia tốc (tính bằng m/s2) tác dụng lên thiết bị trên cả 3 trục vật lý (x, y và z), bao gồm cả lực hấp dẫn. | Phát hiện chuyển động (lắc, nghiêng, v.v.). |
TYPE_AMBIENT_TEMPERATURE |
Phần cứng | Đo nhiệt độ môi trường xung quanh trong phòng theo độ C (°C). Xem phần lưu ý bên dưới. | Giám sát nhiệt độ không khí. |
TYPE_GRAVITY |
Phần mềm hoặc phần cứng | Đo lực hấp dẫn tính bằng m/s2 được áp dụng cho một thiết bị trên cả 3 trục vật lý (x, y, z). | Phát hiện chuyển động (lắc, nghiêng, v.v.). |
TYPE_GYROSCOPE |
Phần cứng | Đo tốc độ xoay của thiết bị theo đơn vị rad/s quanh mỗi trục trong số 3 trục vật lý (x, y và z). | Phát hiện chuyển động xoay (xoay tròn, xoay người, v.v.). |
TYPE_LIGHT |
Phần cứng | Đo mức độ ánh sáng xung quanh (độ chiếu sáng) theo đơn vị lux. | Điều khiển độ sáng màn hình. |
TYPE_LINEAR_ACCELERATION |
Phần mềm hoặc phần cứng | Đo lực gia tốc theo m/s2 được áp dụng cho một thiết bị trên cả 3 trục vật lý (x, y và z), không bao gồm lực hấp dẫn. | Theo dõi gia tốc dọc theo một trục. |
TYPE_MAGNETIC_FIELD |
Phần cứng | Đo từ trường địa từ xung quanh cho cả 3 trục vật lý (x, y, z) theo đơn vị μT. | Tạo la bàn. |
TYPE_ORIENTATION |
Phần mềm | Đo mức độ xoay của thiết bị quanh cả 3 trục vật lý (x, y, z).
Kể từ API cấp 3, bạn có thể lấy ma trận độ nghiêng và ma trận xoay cho một thiết bị bằng cách sử dụng cảm biến trọng lực và cảm biến từ trường cùng với phương thức getRotationMatrix() . |
Xác định vị trí của thiết bị. |
TYPE_PRESSURE |
Phần cứng | Đo áp suất không khí xung quanh tính bằng hPa hoặc mbar. | Theo dõi những thay đổi về áp suất không khí. |
TYPE_PROXIMITY |
Phần cứng | Đo khoảng cách của một vật thể (tính bằng cm) so với màn hình xem của thiết bị. Cảm biến này thường được dùng để xác định xem thiết bị cầm tay có đang được giữ gần tai của một người hay không. | Vị trí điện thoại trong cuộc gọi. |
TYPE_RELATIVE_HUMIDITY |
Phần cứng | Đo độ ẩm tương đối của môi trường xung quanh theo tỷ lệ phần trăm (%). | Giám sát điểm sương, độ ẩm tuyệt đối và độ ẩm tương đối. |
TYPE_ROTATION_VECTOR |
Phần mềm hoặc phần cứng | Đo hướng của thiết bị bằng cách cung cấp 3 phần tử của vectơ xoay của thiết bị. | Phát hiện chuyển động và phát hiện xoay. |
TYPE_TEMPERATURE |
Phần cứng | Đo nhiệt độ của thiết bị theo độ C (°C). Việc triển khai cảm biến này khác nhau giữa các thiết bị và cảm biến này đã được thay thế bằng cảm biến TYPE_AMBIENT_TEMPERATURE trong API cấp 14 |
Giám sát nhiệt độ. |
Khung cảm biến
Bạn có thể truy cập vào các cảm biến này và thu thập dữ liệu cảm biến thô bằng cách sử dụng khung cảm biến Android.
Khung cảm biến là một phần của gói android.hardware
và bao gồm các lớp và giao diện sau:
SensorManager
- Bạn có thể sử dụng lớp này để tạo một phiên bản của dịch vụ cảm biến. Lớp này cung cấp nhiều phương thức để truy cập và liệt kê các cảm biến, đăng ký và huỷ đăng ký trình nghe sự kiện cảm biến, đồng thời thu thập thông tin về hướng. Lớp này cũng cung cấp một số hằng số cảm biến được dùng để báo cáo độ chính xác của cảm biến, đặt tốc độ thu thập dữ liệu và hiệu chỉnh cảm biến.
Sensor
- Bạn có thể dùng lớp này để tạo một thực thể của một cảm biến cụ thể. Lớp này cung cấp nhiều phương thức cho phép bạn xác định các chức năng của cảm biến.
SensorEvent
- Hệ thống sử dụng lớp này để tạo một đối tượng sự kiện cảm biến, cung cấp thông tin về một sự kiện cảm biến. Một đối tượng sự kiện cảm biến bao gồm những thông tin sau: dữ liệu cảm biến thô, loại cảm biến đã tạo sự kiện, độ chính xác của dữ liệu và dấu thời gian cho sự kiện.
SensorEventListener
- Bạn có thể sử dụng giao diện này để tạo 2 phương thức gọi lại nhận thông báo (sự kiện cảm biến) khi giá trị cảm biến thay đổi hoặc khi độ chính xác của cảm biến thay đổi.
Trong một ứng dụng thông thường, bạn sử dụng các API liên quan đến cảm biến này để thực hiện hai tác vụ cơ bản:
- Xác định cảm biến và khả năng của cảm biến
Việc xác định các cảm biến và khả năng của cảm biến trong thời gian chạy sẽ hữu ích nếu ứng dụng của bạn có các tính năng dựa vào các loại hoặc khả năng cảm biến cụ thể. Ví dụ: bạn có thể muốn xác định tất cả các cảm biến có trên thiết bị và vô hiệu hoá mọi tính năng ứng dụng dựa vào các cảm biến không có trên thiết bị. Tương tự, bạn có thể muốn xác định tất cả các cảm biến thuộc một loại nhất định để có thể chọn chế độ triển khai cảm biến có hiệu suất tối ưu cho ứng dụng của mình.
- Giám sát các sự kiện cảm biến
Giám sát các sự kiện cảm biến là cách bạn thu thập dữ liệu cảm biến thô. Sự kiện cảm biến xảy ra mỗi khi cảm biến phát hiện thấy sự thay đổi trong các thông số mà cảm biến đang đo. Một sự kiện cảm biến cung cấp cho bạn 4 thông tin: tên của cảm biến đã kích hoạt sự kiện, dấu thời gian của sự kiện, độ chính xác của sự kiện và dữ liệu cảm biến thô đã kích hoạt sự kiện.
Phạm vi cung cấp cảm biến
Mặc dù phạm vi cung cấp cảm biến có thể khác nhau tuỳ theo thiết bị, nhưng phạm vi này cũng có thể khác nhau giữa các phiên bản Android. Điều này là do các cảm biến Android đã được giới thiệu trong quá trình phát hành một số nền tảng. Ví dụ: nhiều cảm biến đã được giới thiệu trong Android 1.5 (API cấp 3), nhưng một số cảm biến chưa được triển khai và không dùng được cho đến Android 2.3 (API cấp 9). Tương tự, một số cảm biến đã được ra mắt trong Android 2.3 (API cấp 9) và Android 4.0 (API cấp 14). Hai cảm biến đã ngừng hoạt động và được thay thế bằng các cảm biến mới hơn, tốt hơn.
Bảng 2 tóm tắt khả năng cung cấp của từng cảm biến theo từng nền tảng. Chỉ có 4 nền tảng được liệt kê vì đó là những nền tảng có liên quan đến các thay đổi về cảm biến. Các cảm biến được liệt kê là không dùng nữa vẫn có trên các nền tảng tiếp theo (miễn là cảm biến có trên thiết bị), phù hợp với chính sách tương thích về phía trước của Android.
Bảng 2. Phạm vi cung cấp cảm biến theo nền tảng.
Cảm biến | Android 4.0 (API cấp 14) |
Android 2.3 (API cấp 9) |
Android 2.2 (API cấp 8) |
Android 1.5 (API cấp 3) |
---|---|---|---|---|
TYPE_ACCELEROMETER |
Có | Có | Có | Có |
TYPE_AMBIENT_TEMPERATURE |
Có | Không có | Không có | Không có |
TYPE_GRAVITY |
Có | Có | Không có | Không có |
TYPE_GYROSCOPE |
Có | Có | n/a1 | n/a1 |
TYPE_LIGHT |
Có | Có | Có | Có |
TYPE_LINEAR_ACCELERATION |
Có | Có | Không có | Không có |
TYPE_MAGNETIC_FIELD |
Có | Có | Có | Có |
TYPE_ORIENTATION |
Có2 | Có2 | Có2 | Có |
TYPE_PRESSURE |
Có | Có | n/a1 | n/a1 |
TYPE_PROXIMITY |
Có | Có | Có | Có |
TYPE_RELATIVE_HUMIDITY |
Có | Không có | Không có | Không có |
TYPE_ROTATION_VECTOR |
Có | Có | Không có | Không có |
TYPE_TEMPERATURE |
Có2 | Có | Có | Có |
1 Loại cảm biến này được thêm vào Android 1.5 (API cấp 3), nhưng không dùng được cho đến Android 2.3 (API cấp 9).
2 Cảm biến này có sẵn nhưng đã ngừng hoạt động.
Xác định cảm biến và các chức năng của cảm biến
Khung cảm biến Android cung cấp một số phương thức giúp bạn dễ dàng xác định những cảm biến có trên thiết bị trong thời gian chạy. API này cũng cung cấp các phương thức cho phép bạn xác định khả năng của từng cảm biến, chẳng hạn như phạm vi tối đa, độ phân giải và yêu cầu về nguồn điện.
Để xác định các cảm biến trên một thiết bị, trước tiên, bạn cần tham chiếu đến dịch vụ cảm biến. Để thực hiện việc này, bạn tạo một phiên bản của lớp SensorManager
bằng cách gọi phương thức getSystemService()
và truyền đối số SENSOR_SERVICE
. Ví dụ:
Kotlin
private lateinit var sensorManager: SensorManager ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
Java
private SensorManager sensorManager; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Tiếp theo, bạn có thể nhận danh sách mọi cảm biến trên thiết bị bằng cách gọi phương thức getSensorList()
và sử dụng hằng số TYPE_ALL
. Ví dụ:
Kotlin
val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)
Java
List<Sensor> deviceSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
Nếu muốn liệt kê tất cả các cảm biến thuộc một loại nhất định, bạn có thể dùng một hằng số khác thay vì TYPE_ALL
, chẳng hạn như TYPE_GYROSCOPE
, TYPE_LINEAR_ACCELERATION
hoặc TYPE_GRAVITY
.
Bạn cũng có thể xác định xem một loại cảm biến cụ thể có tồn tại trên thiết bị hay không bằng cách sử dụng phương thức getDefaultSensor()
và truyền hằng số loại cho một cảm biến cụ thể. Nếu một thiết bị có nhiều cảm biến thuộc một loại nhất định, thì một trong các cảm biến đó phải được chỉ định làm cảm biến mặc định. Nếu không có cảm biến mặc định cho một loại cảm biến nhất định, thì lệnh gọi phương thức sẽ trả về giá trị rỗng, tức là thiết bị không có loại cảm biến đó. Ví dụ: đoạn mã sau đây kiểm tra xem có từ kế trên thiết bị hay không:
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. }
Lưu ý: Android không yêu cầu nhà sản xuất thiết bị tích hợp bất kỳ loại cảm biến cụ thể nào vào thiết bị chạy Android. Do đó, các thiết bị có thể có nhiều cấu hình cảm biến.
Ngoài việc liệt kê các cảm biến trên thiết bị, bạn có thể dùng các phương thức công khai của lớp Sensor
để xác định các chức năng và thuộc tính của từng cảm biến. Điều này sẽ hữu ích nếu bạn muốn ứng dụng của mình hoạt động theo cách khác tuỳ thuộc vào cảm biến hoặc khả năng cảm biến có trên thiết bị. Ví dụ: bạn có thể dùng các phương thức getResolution()
và getMaximumRange()
để lấy độ phân giải và phạm vi đo tối đa của cảm biến. Bạn cũng có thể sử dụng phương thức getPower()
để biết yêu cầu về nguồn điện của cảm biến.
Hai phương thức công khai đặc biệt hữu ích nếu bạn muốn tối ưu hoá ứng dụng cho các cảm biến của nhà sản xuất khác nhau hoặc các phiên bản khác nhau của một cảm biến. Ví dụ: nếu ứng dụng của bạn cần theo dõi các cử chỉ của người dùng, chẳng hạn như nghiêng và lắc, bạn có thể tạo một bộ quy tắc lọc dữ liệu và các hoạt động tối ưu hoá cho các thiết bị mới hơn có cảm biến trọng lực của một nhà cung cấp cụ thể, và một bộ quy tắc lọc dữ liệu và các hoạt động tối ưu hoá khác cho các thiết bị không có cảm biến trọng lực và chỉ có gia tốc kế. Mã mẫu sau đây cho biết cách bạn có thể sử dụng các phương thức getVendor()
và getVersion()
để thực hiện việc này. Trong mẫu này, chúng ta đang tìm một cảm biến trọng lực có Google LLC là nhà cung cấp và có số phiên bản là 3. Nếu thiết bị không có cảm biến cụ thể đó, chúng tôi sẽ cố gắng sử dụng gia tốc kế.
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. } }
Một phương thức hữu ích khác là phương thức getMinDelay()
, phương thức này trả về khoảng thời gian tối thiểu (tính bằng micro giây) mà cảm biến có thể dùng để cảm nhận dữ liệu. Mọi cảm biến trả về giá trị khác 0 cho phương thức getMinDelay()
đều là cảm biến truyền phát trực tiếp. Các cảm biến truyền phát trực tiếp cảm nhận dữ liệu theo định kỳ và được giới thiệu trong Android 2.3 (API cấp 9). Nếu một cảm biến trả về giá trị 0 khi bạn gọi phương thức getMinDelay()
, thì có nghĩa là cảm biến đó không phải là cảm biến truyền phát trực tiếp vì chỉ báo cáo dữ liệu khi có sự thay đổi về các thông số mà cảm biến đang cảm nhận.
Phương thức getMinDelay()
rất hữu ích vì cho phép bạn xác định tốc độ tối đa mà cảm biến có thể thu thập dữ liệu. Nếu một số tính năng trong ứng dụng của bạn yêu cầu tốc độ thu thập dữ liệu cao hoặc cảm biến truyền phát trực tiếp, bạn có thể dùng phương thức này để xác định xem một cảm biến có đáp ứng các yêu cầu đó hay không, sau đó bật hoặc tắt các tính năng có liên quan trong ứng dụng của bạn cho phù hợp.
Thận trọng: Tốc độ thu thập dữ liệu tối đa của cảm biến không nhất thiết là tốc độ mà khung cảm biến cung cấp dữ liệu cảm biến cho ứng dụng của bạn. Khung cảm biến báo cáo dữ liệu thông qua các sự kiện cảm biến và một số yếu tố ảnh hưởng đến tốc độ mà ứng dụng của bạn nhận được các sự kiện cảm biến. Để biết thêm thông tin, hãy xem phần Giám sát sự kiện cảm biến.
Theo dõi các sự kiện cảm biến
Để theo dõi dữ liệu cảm biến thô, bạn cần triển khai 2 phương thức gọi lại được hiển thị thông qua giao diện SensorEventListener
: onAccuracyChanged()
và onSensorChanged()
. Hệ thống Android sẽ gọi các phương thức này bất cứ khi nào xảy ra những trường hợp sau:
- Độ chính xác của cảm biến thay đổi.
Trong trường hợp này, hệ thống sẽ gọi phương thức
onAccuracyChanged()
, cung cấp cho bạn một thông tin tham chiếu đến đối tượngSensor
đã thay đổi và độ chính xác mới của cảm biến. Độ chính xác được biểu thị bằng một trong bốn hằng số trạng thái:SENSOR_STATUS_ACCURACY_LOW
,SENSOR_STATUS_ACCURACY_MEDIUM
,SENSOR_STATUS_ACCURACY_HIGH
hoặcSENSOR_STATUS_UNRELIABLE
. - Cảm biến báo cáo một giá trị mới.
Trong trường hợp này, hệ thống sẽ gọi phương thức
onSensorChanged()
, cung cấp cho bạn một đối tượngSensorEvent
. Đối tượngSensorEvent
chứa thông tin về dữ liệu cảm biến mới, bao gồm: độ chính xác của dữ liệu, cảm biến đã tạo dữ liệu, dấu thời gian mà dữ liệu được tạo và dữ liệu mới mà cảm biến đã ghi lại.
Đoạn mã sau đây cho biết cách sử dụng phương thức onSensorChanged()
để theo dõi dữ liệu từ cảm biến ánh sáng. Ví dụ này cho thấy dữ liệu thô của cảm biến trong một TextView
được xác định trong tệp main.xml dưới dạng 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); } }
Trong ví dụ này, độ trễ dữ liệu mặc định (SENSOR_DELAY_NORMAL
) được chỉ định khi phương thức registerListener()
được gọi. Độ trễ dữ liệu (hoặc tốc độ lấy mẫu) kiểm soát khoảng thời gian mà các sự kiện cảm biến được gửi đến ứng dụng của bạn thông qua phương thức gọi lại onSensorChanged()
. Độ trễ dữ liệu mặc định phù hợp để theo dõi các thay đổi thông thường về hướng màn hình và sử dụng độ trễ 200.000 micro giây. Bạn có thể chỉ định các độ trễ dữ liệu khác, chẳng hạn như SENSOR_DELAY_GAME
(độ trễ 20.000 micrô giây), SENSOR_DELAY_UI
(độ trễ 60.000 micrô giây) hoặc SENSOR_DELAY_FASTEST
(độ trễ 0 micrô giây). Kể từ Android 3.0 (API cấp 11), bạn cũng có thể chỉ định độ trễ dưới dạng giá trị tuyệt đối (tính bằng micro giây).
Độ trễ mà bạn chỉ định chỉ là độ trễ được đề xuất. Hệ thống Android và các ứng dụng khác có thể thay đổi độ trễ này. Tốt nhất là bạn nên chỉ định độ trễ lớn nhất có thể vì hệ thống thường sử dụng độ trễ nhỏ hơn độ trễ mà bạn chỉ định (tức là bạn nên chọn tốc độ lấy mẫu chậm nhất vẫn đáp ứng được nhu cầu của ứng dụng). Việc sử dụng độ trễ lớn hơn sẽ giảm tải cho bộ xử lý và do đó tiêu thụ ít điện năng hơn.
Không có phương thức công khai nào để xác định tốc độ mà khung cảm biến gửi các sự kiện cảm biến đến ứng dụng của bạn; tuy nhiên, bạn có thể sử dụng dấu thời gian được liên kết với từng sự kiện cảm biến để tính tốc độ lấy mẫu trên nhiều sự kiện. Bạn không cần thay đổi tốc độ lấy mẫu (độ trễ) sau khi đặt. Nếu vì lý do nào đó mà bạn cần thay đổi độ trễ, bạn sẽ phải huỷ đăng ký và đăng ký lại trình nghe cảm biến.
Bạn cũng cần lưu ý rằng ví dụ này sử dụng các phương thức gọi lại onResume()
và onPause()
để đăng ký và huỷ đăng ký trình nghe sự kiện cảm biến. Tốt nhất là bạn nên luôn tắt các cảm biến không cần thiết, đặc biệt là khi hoạt động của bạn bị tạm dừng. Nếu không làm như vậy, pin có thể hết chỉ trong vài giờ vì một số cảm biến có yêu cầu đáng kể về nguồn điện và có thể tiêu thụ pin nhanh chóng. Hệ thống sẽ không tự động tắt các cảm biến khi màn hình tắt.
Xử lý các cấu hình cảm biến khác nhau
Android không chỉ định cấu hình cảm biến tiêu chuẩn cho các thiết bị, tức là nhà sản xuất thiết bị có thể kết hợp bất kỳ cấu hình cảm biến nào mà họ muốn vào các thiết bị chạy Android. Do đó, các thiết bị có thể bao gồm nhiều loại cảm biến trong nhiều cấu hình. Nếu ứng dụng của bạn dựa vào một loại cảm biến cụ thể, bạn phải đảm bảo rằng cảm biến đó có trên thiết bị để ứng dụng có thể chạy thành công.
Bạn có 2 lựa chọn để đảm bảo rằng một cảm biến nhất định có trên thiết bị:
- Phát hiện các cảm biến trong thời gian chạy và bật hoặc tắt các tính năng của ứng dụng cho phù hợp.
- Sử dụng bộ lọc Google Play để nhắm đến các thiết bị có cấu hình cảm biến cụ thể.
Mỗi lựa chọn sẽ được thảo luận trong các phần sau.
Phát hiện cảm biến trong thời gian chạy
Nếu ứng dụng của bạn sử dụng một loại cảm biến cụ thể nhưng không phụ thuộc vào cảm biến đó, bạn có thể dùng khung cảm biến để phát hiện cảm biến trong thời gian chạy, sau đó tắt hoặc bật các tính năng của ứng dụng cho phù hợp. Ví dụ: một ứng dụng chỉ đường có thể sử dụng cảm biến nhiệt độ, cảm biến áp suất, cảm biến GPS và cảm biến từ trường để hiển thị nhiệt độ, áp suất khí quyển, vị trí và hướng la bàn. Nếu thiết bị không có cảm biến áp suất, bạn có thể dùng khung cảm biến để phát hiện sự vắng mặt của cảm biến áp suất trong thời gian chạy, sau đó vô hiệu hoá phần giao diện người dùng của ứng dụng hiển thị áp suất. Ví dụ: đoạn mã sau đây kiểm tra xem có cảm biến áp suất trên thiết bị hay không:
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. }
Sử dụng bộ lọc của Google Play để nhắm đến các cấu hình cảm biến cụ thể
Nếu đang xuất bản ứng dụng trên Google Play, bạn có thể sử dụng phần tử <uses-feature>
trong tệp kê khai để lọc ứng dụng khỏi những thiết bị không có cấu hình cảm biến phù hợp cho ứng dụng của bạn. Phần tử <uses-feature>
có một số bộ mô tả phần cứng cho phép bạn lọc các ứng dụng dựa trên sự hiện diện của các cảm biến cụ thể. Các cảm biến mà bạn có thể liệt kê bao gồm: gia tốc kế, khí áp kế, la bàn (từ trường), con quay hồi chuyển, ánh sáng và độ gần. Sau đây là ví dụ về mục nhập tệp kê khai lọc các ứng dụng không có gia tốc kế:
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true" />
Nếu bạn thêm phần tử và bộ mô tả này vào tệp kê khai của ứng dụng, thì người dùng sẽ chỉ thấy ứng dụng của bạn trên Google Play nếu thiết bị của họ có gia tốc kế.
Bạn chỉ nên đặt giá trị mô tả thành android:required="true"
nếu ứng dụng của bạn hoàn toàn dựa vào một cảm biến cụ thể. Nếu ứng dụng của bạn dùng một cảm biến cho một số chức năng nhưng vẫn chạy mà không cần cảm biến, thì bạn nên liệt kê cảm biến đó trong phần tử <uses-feature>
nhưng đặt giá trị mô tả thành android:required="false"
. Điều này giúp đảm bảo rằng các thiết bị có thể cài đặt ứng dụng của bạn ngay cả khi chúng không có cảm biến cụ thể đó. Đây cũng là một phương pháp hay nhất về quản lý dự án giúp bạn theo dõi các tính năng mà ứng dụng của bạn sử dụng.
Xin lưu ý rằng nếu ứng dụng của bạn sử dụng một cảm biến cụ thể nhưng vẫn chạy mà không cần cảm biến đó, thì bạn nên phát hiện cảm biến đó trong thời gian chạy và tắt hoặc bật các tính năng của ứng dụng cho phù hợp.
Hệ toạ độ cảm biến
Nói chung, khung cảm biến sử dụng hệ toạ độ 3 trục tiêu chuẩn để biểu thị các giá trị dữ liệu. Đối với hầu hết các cảm biến, hệ toạ độ được xác định tương ứng với màn hình của thiết bị khi thiết bị được giữ ở hướng mặc định (xem hình 1). Khi thiết bị được giữ ở hướng mặc định, trục X nằm ngang và hướng sang phải, trục Y thẳng đứng và hướng lên trên, còn trục Z hướng ra ngoài mặt màn hình. Trong hệ thống này, các toạ độ phía sau màn hình có giá trị Z âm. Hệ toạ độ này được dùng bởi các cảm biến sau:

Hình 1. Hệ toạ độ (tương ứng với một thiết bị) mà Sensor API sử dụng.
- Cảm biến gia tốc
- Cảm biến trọng lực
- Con quay hồi chuyển
- Cảm biến gia tốc tuyến tính
- Cảm biến từ trường địa từ
Điểm quan trọng nhất cần hiểu về hệ toạ độ này là các trục không được hoán đổi khi hướng màn hình của thiết bị thay đổi, tức là hệ toạ độ của cảm biến không bao giờ thay đổi khi thiết bị di chuyển. Hành vi này giống với hành vi của hệ toạ độ OpenGL.
Một điểm khác cần lưu ý là ứng dụng của bạn không được giả định rằng hướng tự nhiên (mặc định) của thiết bị là hướng dọc. Hướng tự nhiên của nhiều thiết bị máy tính bảng là hướng ngang. Và hệ toạ độ cảm biến luôn dựa trên hướng tự nhiên của thiết bị.
Cuối cùng, nếu ứng dụng của bạn so khớp dữ liệu cảm biến với nội dung hiển thị trên màn hình, bạn cần sử dụng phương thức getRotation()
để xác định hướng xoay màn hình, sau đó sử dụng phương thức remapCoordinateSystem()
để ánh xạ toạ độ cảm biến với toạ độ màn hình. Bạn cần làm việc này ngay cả khi tệp kê khai của bạn chỉ định chế độ hiển thị dọc.
Lưu ý: Một số cảm biến và phương thức sử dụng hệ toạ độ tương ứng với khung tham chiếu của thế giới (thay vì khung tham chiếu của thiết bị). Các cảm biến và phương thức này trả về dữ liệu biểu thị chuyển động của thiết bị hoặc vị trí của thiết bị so với trái đất. Để biết thêm thông tin, hãy xem phương thức getOrientation()
, phương thức getRotationMatrix()
, Cảm biến hướng và Cảm biến vectơ xoay.
Giới hạn tốc độ của cảm biến
Để bảo vệ thông tin có thể nhạy cảm về người dùng, nếu ứng dụng của bạn nhắm đến Android 12 (API cấp 31) trở lên, thì hệ thống sẽ giới hạn tốc độ làm mới dữ liệu từ một số cảm biến chuyển động và cảm biến vị trí. Dữ liệu này bao gồm các giá trị do gia tốc kế, con quay hồi chuyển và cảm biến từ trường địa từ của thiết bị ghi lại.
Giới hạn tốc độ làm mới phụ thuộc vào cách bạn truy cập vào dữ liệu cảm biến:
- Nếu bạn gọi phương thức
registerListener()
để giám sát các sự kiện cảm biến, thì tốc độ lấy mẫu cảm biến sẽ bị giới hạn ở 200 Hz. Điều này đúng với tất cả các biến thể bị quá tải của phương thứcregisterListener()
. - Nếu bạn sử dụng lớp
SensorDirectChannel
, tốc độ lấy mẫu của cảm biến sẽ bị giới hạn ởRATE_NORMAL
, thường là khoảng 50 Hz.
Nếu ứng dụng của bạn cần thu thập dữ liệu cảm biến chuyển động ở tốc độ cao hơn, bạn phải khai báo quyền HIGH_SAMPLING_RATE_SENSORS
, như trong đoạn mã sau. Nếu không, nếu ứng dụng của bạn cố gắng thu thập dữ liệu cảm biến chuyển động ở tốc độ cao hơn mà không khai báo quyền này, thì sẽ xảy ra lỗi SecurityException
.
AndroidManifest.xml
<manifest ...> <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"/> <application ...> ... </application> </manifest>
Các phương pháp hay nhất để truy cập và sử dụng cảm biến
Khi thiết kế việc triển khai cảm biến, hãy nhớ tuân thủ các nguyên tắc được thảo luận trong phần này. Đây là các phương pháp hay nhất được đề xuất cho bất kỳ ai đang sử dụng khung cảm biến để truy cập vào cảm biến và thu thập dữ liệu cảm biến.
Chỉ thu thập dữ liệu cảm biến ở nền trước
Trên các thiết bị chạy Android 9 (API cấp 28) trở lên, các ứng dụng chạy ở chế độ nền sẽ có những hạn chế sau:
- Các cảm biến sử dụng chế độ báo cáo liên tục, chẳng hạn như gia tốc kế và con quay hồi chuyển, sẽ không nhận được các sự kiện.
- Các cảm biến sử dụng chế độ báo cáo khi có thay đổi hoặc một lần sẽ không nhận được sự kiện.
Do những hạn chế này, tốt nhất là bạn nên phát hiện các sự kiện cảm biến khi ứng dụng của bạn đang ở nền trước hoặc trong dịch vụ trên nền trước.
Huỷ đăng ký trình nghe cảm biến
Nhớ huỷ đăng ký trình nghe của cảm biến khi bạn dùng xong cảm biến hoặc khi hoạt động của cảm biến tạm dừng. Nếu một trình nghe cảm biến được đăng ký và hoạt động của trình nghe này bị tạm dừng, thì cảm biến sẽ tiếp tục thu thập dữ liệu và sử dụng tài nguyên pin, trừ phi bạn huỷ đăng ký cảm biến. Đoạn mã sau đây cho biết cách sử dụng phương thức onPause()
để huỷ đăng ký một trình nghe:
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); }
Để biết thêm thông tin, hãy xem unregisterListener(SensorEventListener)
.
Kiểm thử bằng Trình mô phỏng Android
Trình mô phỏng Android có một bộ chế độ điều khiển cảm biến ảo cho phép bạn kiểm thử các cảm biến như gia tốc kế, nhiệt độ môi trường, từ kế, độ gần, ánh sáng, v.v.
Trình mô phỏng sử dụng kết nối với một thiết bị Android đang chạy ứng dụng SdkControllerSensor. Xin lưu ý rằng ứng dụng này chỉ có trên các thiết bị chạy Android 4.0 (API cấp 14) trở lên. (Nếu thiết bị đang chạy Android 4.0, thì thiết bị đó phải đã cài đặt Bản sửa đổi 2.) Ứng dụng SdkControllerSensor giám sát các thay đổi trong cảm biến trên thiết bị và truyền các thay đổi đó đến trình mô phỏng. Sau đó, trình mô phỏng sẽ được chuyển đổi dựa trên các giá trị mới mà trình mô phỏng nhận được từ các cảm biến trên thiết bị của bạn.
Bạn có thể xem mã nguồn của ứng dụng SdkControllerSensor ở vị trí sau:
$ your-android-sdk-directory/tools/apps/SdkController
Để chuyển dữ liệu giữa thiết bị và trình mô phỏng, hãy làm theo các bước sau:
- Kiểm tra để đảm bảo bạn đã bật tính năng gỡ lỗi qua USB trên thiết bị.
- Kết nối thiết bị với máy phát triển bằng cáp USB.
- Khởi động ứng dụng SdkControllerSensor trên thiết bị của bạn.
- Trong ứng dụng, hãy chọn các cảm biến mà bạn muốn mô phỏng.
Chạy lệnh
adb
sau:- Khởi động trình mô phỏng. Giờ đây, bạn có thể áp dụng các phép biến đổi cho trình mô phỏng bằng cách di chuyển thiết bị.
$ adb forward tcp:1968 tcp:1968
Lưu ý: Nếu các chuyển động mà bạn thực hiện trên thiết bị thực không chuyển đổi trình mô phỏng, hãy thử chạy lại lệnh adb
ở bước 5.
Để biết thêm thông tin, hãy xem hướng dẫn về Trình mô phỏng Android.
Đừng chặn phương thức onSensorChanged()
Dữ liệu cảm biến có thể thay đổi với tốc độ cao, tức là hệ thống có thể gọi phương thức onSensorChanged(SensorEvent)
khá thường xuyên. Tốt nhất là bạn nên thực hiện càng ít thao tác càng tốt trong phương thức onSensorChanged(SensorEvent)
để không chặn phương thức này. Nếu ứng dụng của bạn yêu cầu bạn lọc dữ liệu hoặc giảm dữ liệu cảm biến, bạn nên thực hiện việc đó bên ngoài phương thức onSensorChanged(SensorEvent)
.
Tránh sử dụng các phương thức hoặc loại cảm biến không được dùng nữa
Một số phương thức và hằng số đã bị ngừng sử dụng.
Cụ thể, loại cảm biến TYPE_ORIENTATION
không còn được dùng nữa. Để lấy dữ liệu về hướng, bạn nên sử dụng phương thức getOrientation()
. Tương tự, loại cảm biến TYPE_TEMPERATURE
cũng không còn được dùng nữa. Bạn nên sử dụng loại cảm biến TYPE_AMBIENT_TEMPERATURE
thay vì loại cảm biến này trên các thiết bị đang chạy Android 4.0.
Xác minh các cảm biến trước khi sử dụng
Luôn xác minh rằng một cảm biến tồn tại trên thiết bị trước khi bạn cố gắng thu thập dữ liệu từ thiết bị đó. Đừng giả định rằng một cảm biến tồn tại chỉ vì đó là một cảm biến thường được dùng. Nhà sản xuất thiết bị không bắt buộc phải cung cấp bất kỳ cảm biến cụ thể nào trong thiết bị của họ.
Chọn độ trễ của cảm biến một cách cẩn thận
Khi đăng ký một cảm biến bằng phương thức registerListener()
, hãy nhớ chọn tốc độ truyền phù hợp với ứng dụng hoặc trường hợp sử dụng của bạn. Cảm biến có thể cung cấp dữ liệu ở tốc độ rất cao. Việc cho phép hệ thống gửi thêm dữ liệu mà bạn không cần sẽ lãng phí tài nguyên hệ thống và tiêu hao pin.