Visão geral de sensores

A maioria dos dispositivos Android tem sensores integrados que medem movimento, orientação e várias condições ambientais. Esses sensores são capazes de fornecer dados brutos com alta precisão e exatidão, e são úteis se você quiser monitorar o movimento ou posicionamento tridimensional do dispositivo ou se quiser monitorar mudanças no ambiente ao redor de um dispositivo. Por exemplo, um jogo pode rastrear leituras do sensor de gravidade de um dispositivo para inferir gestos e movimentos complexos do usuário, como inclinação, agitação, rotação ou balanço. Da mesma forma, um aplicativo de clima pode usar os sensores de temperatura e umidade de um dispositivo para calcular e informar o ponto de condensação, ou um aplicativo de viagem pode usar o sensor de campo geomagnético e o acelerômetro para informar uma direção de bússola.

A plataforma Android é compatível com três categorias amplas de sensores:

  • Sensores de movimento

    Esses sensores medem forças rotacionais e de aceleração em três eixos. Essa categoria inclui acelerômetros, sensores de gravidade, giroscópios e sensores de vetor rotacional.

  • Sensores ambientais

    Esses sensores medem vários parâmetros ambientais, como temperatura e pressão do ar ambiente, iluminação e umidade. Essa categoria inclui barômetros, fotômetros e termômetros.

  • Sensores de posição

    Esses sensores medem a posição física de um dispositivo. Essa categoria inclui sensores de orientação e magnetômetros.

É possível acessar os sensores disponíveis no dispositivo e adquirir dados do sensor brutos usando o framework de sensores do Android. O framework de sensores oferece várias classes e interfaces que ajudam a realizar uma ampla variedade de tarefas relacionadas a sensores. Por exemplo, você pode usar o framework para as seguintes ações:

  • Determinar quais sensores estão disponíveis em um dispositivo.
  • Determinar os recursos de um sensor individual, como alcance máximo, fabricante, requisitos de energia e resolução.
  • Coletar dados brutos do sensor e definir a taxa mínima de velocidade dessa coleta.
  • Registrar e cancelar o registro dos listeners de eventos que monitoram mudanças do sensor.

Esse tópico traz uma visão geral dos sensores disponíveis na plataforma Android. Ele também oferece uma introdução ao framework de sensor.

Introdução aos sensores

O framework de sensor do Android permite acessar vários tipos de sensores. Alguns desses sensores são baseados em hardware e outros em software. Os sensores baseados em hardware são componentes físicos integrados a um aparelho celular ou tablet. Eles derivam os dados medindo diretamente propriedades ambientais específicas, como aceleração, intensidade do campo geomagnético ou mudança angular. Os sensores baseados em software não são dispositivos físicos, embora imitem sensores baseados em hardware. Os sensores baseados em software derivam os dados de um ou mais sensores baseados em hardware e, às vezes, são chamados de sensores virtuais ou sintéticos. O sensor de aceleração linear e o sensor de gravidade são exemplos de sensores baseados em software. A Tabela 1 resume os sensores compatíveis com a plataforma Android.

Alguns dispositivos Android têm todos os tipos de sensores. Por exemplo, a maioria dos smartphones e tablets tem um acelerômetro e um magnetômetro, mas poucos dispositivos têm barômetros ou termômetros. Além disso, um dispositivo pode ter mais de um sensor de um determinado tipo. Por exemplo, um dispositivo pode ter dois sensores de gravidade, cada um com um alcance diferente.

Tabela 1. Tipos de sensor compatíveis com a plataforma Android.

Sensor Tipo Descrição Usos comuns
TYPE_ACCELEROMETER Hardware Mede a força de aceleração em m/s2 aplicada a um dispositivo em todos os três eixos físicos (x, y e z), incluindo a força da gravidade. Detecção de movimento (agitação, inclinação etc.).
TYPE_AMBIENT_TEMPERATURE Hardware Mede a temperatura ambiente em graus Celsius (°C). Veja a observação abaixo. Monitoramento das temperaturas do ar.
TYPE_GRAVITY Software ou hardware Mede a força da gravidade em m/s2 aplicada a um dispositivo nos três eixos físicos (x, y, z). Detecção de movimento (agitação, inclinação etc.).
TYPE_GYROSCOPE Hardware Mede a taxa de rotação de um dispositivo em rad/s em torno de cada um dos três eixos físicos (x, y e z). Detecção de rotação (giro, volta etc.).
TYPE_LIGHT Hardware Mede o nível de luz ambiente (iluminação) em lx. Controle do brilho da tela.
TYPE_LINEAR_ACCELERATION Software ou hardware Mede a força de aceleração em m/s2 aplicada a um dispositivo nos três eixos físicos (x, y e z), excluindo a força da gravidade. Monitoramento da aceleração em um único eixo.
TYPE_MAGNETIC_FIELD Hardware Mede o campo geomagnético do ambiente para os três eixos físicos (x, y, z) em μT. Criação de uma bússola.
TYPE_ORIENTATION Software Mede os graus de rotação que um dispositivo faz em torno dos três eixos físicos (x, y, z). A partir do nível 3 da API, é possível receber a matriz de inclinação e a matriz de rotação de um dispositivo usando o sensor de gravidade e o sensor de campo geomagnético em conjunto com o método getRotationMatrix(). Determinação da posição do dispositivo.
TYPE_PRESSURE Hardware Mede a pressão do ar ambiente em hPa ou mbar. Monitoramento das mudanças na pressão do ar.
TYPE_PROXIMITY Hardware Mede a proximidade de um objeto em cm em relação à tela de visualização de um dispositivo. Esse sensor normalmente é usado para determinar se um aparelho está sendo segurado perto do ouvido de uma pessoa. Posição do smartphone durante uma chamada.
TYPE_RELATIVE_HUMIDITY Hardware Mede a umidade relativa do ar em porcentagem (%). Monitoramento de ponto de condensação, umidade absoluta e relativa.
TYPE_ROTATION_VECTOR Software ou hardware Mede a orientação de um dispositivo fornecendo os três elementos do vetor de rotação do dispositivo. Detecção de movimento e de rotação.
TYPE_TEMPERATURE Hardware Mede a temperatura do dispositivo em graus Celsius (°C). A implementação desse sensor varia de acordo com o dispositivo, e ele foi substituído pelo sensor TYPE_AMBIENT_TEMPERATURE no nível da API 14. Monitoramento de temperaturas.

Framework de sensor

É possível acessar esses sensores e coletar dados brutos por meio do framework de sensor do Android. O framework de sensores faz parte do pacote android.hardware e inclui as seguintes classes e interfaces:

SensorManager
Use esta classe para criar uma instância do serviço do sensor. Essa classe fornece vários métodos para acessar e listar sensores, registrar e cancelar o registro de listeners de eventos de sensor e adquirir informações de orientação. Essa classe também fornece várias constantes de sensor que são usadas para informar a precisão do sensor, definir taxas de aquisição de dados e calibrar sensores.
Sensor
Use esta classe para criar uma instância de um sensor específico. Essa classe oferece vários métodos que permitem determinar os recursos de um sensor.
SensorEvent
O sistema usa essa classe para criar um objeto de evento de sensor, que fornece informações sobre um evento de sensor. Um objeto de evento de sensor inclui as seguintes informações: os dados brutos do sensor, o tipo de sensor que gerou o evento, a precisão dos dados e o carimbo de data/hora do evento.
SensorEventListener
Você pode usar essa interface para criar dois métodos de callback que recebem notificações (eventos de sensor) quando os valores ou a precisão do sensor mudam.

Em um aplicativo normal, essas APIs relacionadas ao sensor são usadas para realizar duas tarefas básicas:

  • Como identificar sensores e os recursos deles

    Identificar sensores e recursos de sensores no ambiente de execução é útil se o aplicativo tiver recursos que dependem de tipos ou recursos de sensores específicos. Por exemplo, talvez você queira identificar todos os sensores presentes em um dispositivo e desativar todos os recursos do aplicativo que dependem de sensores que não estão presentes. Da mesma forma, talvez você queira identificar todos os sensores de um determinado tipo para escolher a implementação de sensor que tenha o desempenho ideal para seu aplicativo.

  • Monitorar eventos de sensor

    Para coletar dados brutos, você monitora os eventos do sensor. Um evento de sensor ocorre sempre que um sensor detecta uma mudança nos parâmetros que está medindo. Um evento de sensor fornece quatro informações: o nome do sensor que acionou o evento, o carimbo de data/hora do evento, a precisão do evento e os dados brutos do sensor que acionou o evento.

Disponibilidade do sensor

Embora a disponibilidade do sensor varie de acordo com o dispositivo, ela também pode variar entre as versões do Android. Isso ocorre porque os sensores do Android foram introduzidos ao longo de várias versões da plataforma. Por exemplo, muitos sensores foram introduzidos no Android 1.5 (nível 3 da API), mas alguns não foram implementados e não estavam disponíveis para uso até o Android 2.3 (nível 9 da API). Da mesma forma, vários sensores foram introduzidos no Android 2.3 (nível 9 da API) e no Android 4.0 (nível 14 da API). Dois sensores foram descontinuados e substituídos por sensores mais novos e melhores.

A Tabela 2 resume a disponibilidade de cada sensor de acordo com a plataforma. Apenas quatro plataformas estão listadas porque são as que envolvem mudanças no sensor. Os sensores listados como descontinuados ainda estão disponíveis nas plataformas subsequentes (desde que o sensor esteja presente em um dispositivo), o que está de acordo com a política de compatibilidade com versões futuras do Android.

Tabela 2. Disponibilidade do sensor por plataforma.

Sensor Android 4.0
(API nível 14)
Android 2.3
(API nível 9)
Android 2.2
(API nível 8)
Android 1.5
(API nível 3)
TYPE_ACCELEROMETER Sim Sim Sim Sim
TYPE_AMBIENT_TEMPERATURE Sim n/d n/a n/a
TYPE_GRAVITY Sim Sim n/d n/a
TYPE_GYROSCOPE Sim Sim n/d1 n/a1
TYPE_LIGHT Sim Sim Sim Sim
TYPE_LINEAR_ACCELERATION Sim Sim n/d n/a
TYPE_MAGNETIC_FIELD Sim Sim Sim Sim
TYPE_ORIENTATION Sim2 Sim2 Sim2 Sim
TYPE_PRESSURE Sim Sim n/d1 n/a1
TYPE_PROXIMITY Sim Sim Sim Sim
TYPE_RELATIVE_HUMIDITY Sim n/d n/a n/a
TYPE_ROTATION_VECTOR Sim Sim n/d n/a
TYPE_TEMPERATURE Sim2 Sim Sim Sim

1 Esse tipo de sensor foi adicionado no Android 1.5 (nível 3 da API), mas não estava disponível para uso até o Android 2.3 (nível 9 da API).

2 Esse sensor está disponível, mas foi descontinuado.

Identificar sensores e recursos do sensor

O framework de sensores do Android oferece vários métodos que facilitam a determinação, durante a execução, de quais sensores estão em um dispositivo. A API também fornece métodos que permitem determinar os recursos de cada sensor, como o alcance máximo, a resolução e os requisitos de energia.

Para identificar os sensores que estão em um dispositivo, primeiro você precisa receber uma referência ao serviço de sensor. Para fazer isso, crie uma instância da classe SensorManager chamando o método getSystemService() e transmitindo o argumento SENSOR_SERVICE. Exemplo:

Kotlin

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

Java

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

Em seguida, você pode receber uma lista de todos os sensores em um dispositivo chamando o método getSensorList() e usando a constante TYPE_ALL. Exemplo:

Kotlin

val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)

Java

List<Sensor> deviceSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);

Se você quiser listar todos os sensores de um determinado tipo, use outra constante em vez de TYPE_ALL, como TYPE_GYROSCOPE, TYPE_LINEAR_ACCELERATION ou TYPE_GRAVITY.

Também é possível determinar se um tipo específico de sensor existe em um dispositivo usando o método getDefaultSensor() e transmitindo a constante de tipo para um sensor específico. Se um dispositivo tiver mais de um sensor de um determinado tipo, um deles precisará ser designado como o sensor padrão. Se não houver um sensor padrão para um determinado tipo, a chamada de método vai retornar nulo, o que significa que o dispositivo não tem esse tipo de sensor. Por exemplo, o código a seguir verifica se há um magnetômetro em um dispositivo:

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

Observação:o Android não exige que os fabricantes de dispositivos criem tipos específicos de sensores nos dispositivos Android. Portanto, os dispositivos podem ter uma ampla variedade de configurações de sensores.

Além de listar os sensores que estão em um dispositivo, você pode usar os métodos públicos da classe Sensor para determinar os recursos e atributos de sensores individuais. Isso é útil se você quiser que o aplicativo se comporte de maneira diferente com base nos sensores ou recursos de sensor disponíveis em um dispositivo. Por exemplo, você pode usar os métodos getResolution() e getMaximumRange() para conferir a resolução e o intervalo máximo de medição de um sensor. Também é possível usar o método getPower() para conferir os requisitos de energia de um sensor.

Dois dos métodos públicos são particularmente úteis se você quiser otimizar seu aplicativo para sensores de fabricantes diferentes ou versões diferentes de um sensor. Por exemplo, se o aplicativo precisa monitorar interações do usuário, como inclinação e agitação, você pode criar um conjunto de regras de filtragem de dados e otimizações para dispositivos mais recentes que tenham um sensor de gravidade de um fornecedor específico e outro conjunto de regras de filtragem de dados e otimizações para dispositivos que não tenham um sensor de gravidade e tenham apenas um acelerômetro. O exemplo de código a seguir mostra como usar os métodos getVendor() e getVersion() para fazer isso. Nesta amostra, estamos procurando um sensor de gravidade que liste a Google LLC como fornecedora e tenha o número de versão 3. Se esse sensor específico não estiver presente no dispositivo, tentamos usar o acelerômetro.

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

Outro método útil é o getMinDelay(), que retorna o intervalo de tempo mínimo (em microssegundos) que um sensor pode usar para detectar dados. Qualquer sensor que retorne um valor diferente de zero para o método getMinDelay() é um sensor de streaming. Os sensores de streaming detectam dados em intervalos regulares e foram introduzidos no Android 2.3 (nível 9 da API). Se um sensor retornar zero quando você chamar o método getMinDelay(), isso significa que ele não é um sensor de streaming, porque ele só informa dados quando há uma mudança nos parâmetros que ele está detectando.

O método getMinDelay() é útil porque permite determinar a taxa máxima em que um sensor pode adquirir dados. Se determinados recursos do aplicativo exigirem altas taxas de aquisição de dados ou um sensor de streaming, use esse método para determinar se um sensor atende a esses requisitos e, em seguida, ativar ou desativar os recursos relevantes no aplicativo, conforme necessário.

Cuidado:a taxa máxima de aquisição de dados de um sensor não é necessariamente a taxa em que o framework do sensor fornece dados do sensor ao aplicativo. O framework de sensores informa dados por eventos de sensor, e vários fatores influenciam a taxa em que o aplicativo recebe esses eventos. Para saber mais, consulte Monitorar eventos do sensor.

Monitorar eventos do sensor

Para monitorar dados brutos do sensor, é preciso implementar dois métodos de callback que são expostos pela interface SensorEventListener: onAccuracyChanged() e onSensorChanged(). O sistema Android chama esses métodos sempre que ocorre o seguinte:

O código a seguir mostra como usar o método onSensorChanged() para monitorar dados do sensor de luz. Este exemplo mostra os dados brutos do sensor em uma TextView definida no arquivo main.xml como 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);
    }
}

Nesse exemplo, o atraso de dados padrão (SENSOR_DELAY_NORMAL) é especificado quando o método registerListener() é invocado. O atraso de dados (ou taxa de amostragem) controla o intervalo em que os eventos do sensor são enviados ao aplicativo pelo método de callback onSensorChanged(). O atraso de dados padrão é adequado para monitorar mudanças típicas de orientação da tela e usa um atraso de 200.000 microssegundos. É possível especificar outros atrasos de dados, como SENSOR_DELAY_GAME (atraso de 20.000 microssegundos), SENSOR_DELAY_UI (atraso de 60.000 microssegundos) ou SENSOR_DELAY_FASTEST (atraso de 0 microssegundo). A partir do Android 3.0 (nível 11 da API), também é possível especificar o atraso como um valor absoluto (em microssegundos).

O atraso especificado é apenas uma sugestão. O sistema Android e outros aplicativos podem alterar esse atraso. Como prática recomendada, especifique o maior atraso possível, porque o sistema normalmente usa um atraso menor do que o especificado. Ou seja, escolha a taxa de amostragem mais lenta que ainda atenda às necessidades do aplicativo. O uso de um atraso maior impõe uma carga menor ao processador e, portanto, usa menos energia.

Não há um método público para determinar a taxa em que o framework do sensor está enviando eventos de sensor para o aplicativo. No entanto, é possível usar os carimbos de data/hora associados a cada evento de sensor para calcular a taxa de amostragem em vários eventos. Não é necessário mudar a taxa de amostragem (atraso) depois de defini-la. Se, por algum motivo, você precisar mudar o atraso, será necessário cancelar o registro e registrar novamente o listener do sensor.

Também é importante observar que este exemplo usa os métodos de callback onResume() e onPause() para registrar e cancelar o registro do listener de eventos do sensor. Como prática recomendada, sempre desative os sensores que não são necessários, especialmente quando a atividade estiver pausada. Se você não fizer isso, a bateria pode acabar em poucas horas, porque alguns sensores têm requisitos de energia substanciais e podem consumir a bateria rapidamente. O sistema não desativa os sensores automaticamente quando a tela é desligada.

Gerenciar diferentes configurações de sensores

O Android não especifica uma configuração de sensor padrão para dispositivos, o que significa que os fabricantes podem incorporar qualquer configuração de sensor que quiserem nos dispositivos Android. Como resultado, os dispositivos podem incluir uma variedade de sensores em uma ampla gama de configurações. Se o aplicativo depende de um tipo específico de sensor, é preciso garantir que ele esteja presente no dispositivo para que o app seja executado corretamente.

Há duas opções para verificar se um determinado sensor está presente em um dispositivo:

  • Detectar os sensores durante a execução e ativar ou desativar os recursos do aplicativo conforme apropriado.
  • Usar os filtros do Google Play para segmentar os dispositivos que têm configurações de sensor específicas.

Cada opção é discutida nas seções a seguir.

Detectar sensores durante a execução

Se o aplicativo usa um tipo específico de sensor, mas não depende dele, use o framework de sensores para detectar o sensor no momento da execução e, em seguida, desativar ou ativar os recursos do aplicativo, conforme apropriado. Por exemplo, um aplicativo de navegação pode usar os sensores de temperatura, pressão, GPS e campo geomagnético para mostrar a temperatura, a pressão barométrica, a localização e a direção da bússola. Se um dispositivo não tiver um sensor de pressão, use o framework de sensores para detectar a ausência do sensor de pressão no momento da execução e desativar a parte da interface do aplicativo que mostra a pressão. Por exemplo, o código a seguir verifica se há um sensor de pressão em um dispositivo:

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

Usar os filtros do Google Play para segmentar configurações de sensor específicas

Se você estiver publicando seu aplicativo no Google Play, use o elemento <uses-feature> no arquivo de manifesto para filtrar seu aplicativo de dispositivos que não têm a configuração de sensor adequada para ele. O elemento <uses-feature> tem vários descritores de hardware que permitem filtrar aplicativos com base na presença de sensores específicos. Os sensores que você pode listar incluem: acelerômetro, barômetro, bússola (campo geomagnético), giroscópio, luz e proximidade. Veja a seguir um exemplo de entrada de manifesto que filtra apps que não têm um acelerômetro:

<uses-feature android:name="android.hardware.sensor.accelerometer"
              android:required="true" />

Se você adicionar esse elemento e descritor ao manifesto do aplicativo, os usuários só vão ver seu aplicativo no Google Play se o dispositivo deles tiver um acelerômetro.

Defina o descritor como android:required="true" somente se o aplicativo depender totalmente de um sensor específico. Se o aplicativo usa um sensor para alguma funcionalidade, mas ainda é executado sem ele, liste o sensor no elemento <uses-feature>, mas defina o descritor como android:required="false". Isso ajuda a garantir que os dispositivos possam instalar o app mesmo que não tenham esse sensor específico. Essa também é uma prática recomendada de gerenciamento de projetos que ajuda a acompanhar os recursos usados pelo aplicativo. Se o aplicativo usa um sensor específico, mas ainda funciona sem ele, detecte o sensor no momento da execução e desative ou ative os recursos do aplicativo conforme necessário.

Sistema de coordenadas do sensor

Em geral, o framework do sensor usa um sistema de coordenadas padrão de três eixos para expressar valores de dados. Para a maioria dos sensores, o sistema de coordenadas é definido em relação à tela do dispositivo quando ele é mantido na orientação padrão (consulte a Figura 1). Quando um dispositivo é mantido na orientação padrão, o eixo X é horizontal e aponta para a direita, o eixo Y é vertical e aponta para cima, e o eixo Z aponta para fora da face da tela. Nesse sistema, as coordenadas atrás da tela têm valores Z negativos. Esse sistema de coordenadas é usado pelos seguintes sensores:

Figura 1. Sistema de coordenadas (relativo a um dispositivo) usado pela API Sensor.

O ponto mais importante a ser entendido sobre esse sistema de coordenadas é que os eixos não são trocados quando a orientação da tela do dispositivo muda. Ou seja, o sistema de coordenadas do sensor nunca muda conforme o dispositivo se move. Esse comportamento é o mesmo do sistema de coordenadas do OpenGL.

Outro ponto a ser entendido é que o aplicativo não deve presumir que a orientação natural (padrão) de um dispositivo é retrato. A orientação natural de muitos tablets é a paisagem. E o sistema de coordenadas do sensor é sempre baseado na orientação natural de um dispositivo.

Por fim, se o aplicativo corresponder os dados do sensor à exibição na tela, use o método getRotation() para determinar a rotação da tela e, em seguida, use o método remapCoordinateSystem() para mapear as coordenadas do sensor para as coordenadas da tela. Isso é necessário mesmo que o manifesto especifique apenas a exibição em modo retrato.

Observação:alguns sensores e métodos usam um sistema de coordenadas relativo ao referencial do mundo, em vez do referencial do dispositivo. Esses sensores e métodos retornam dados que representam o movimento ou a posição do dispositivo em relação à Terra. Para mais informações, consulte os métodos getOrientation() e getRotationMatrix(), o sensor de orientação e o sensor de vetor de rotação.

Limitação de taxa do sensor

Para proteger informações potencialmente sensíveis sobre os usuários, se o app for direcionado ao Android 12 (nível 31 da API) ou versões mais recentes, o sistema vai limitar a taxa de atualização de dados de determinados sensores de movimento e de posição. Esses dados incluem valores registrados pelo acelerômetro, giroscópio e sensor de campo geomagnético do dispositivo.

O limite da taxa de atualização depende de como você acessa os dados do sensor:

Se o app precisar coletar dados do sensor de movimento em uma taxa mais alta, declare a permissão HIGH_SAMPLING_RATE_SENSORS, conforme mostrado no snippet de código abaixo. Caso contrário, se o app tentar coletar dados do sensor de movimento em uma taxa mais alta sem declarar essa permissão, uma SecurityException vai ocorrer.

AndroidManifest.xml

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

Práticas recomendadas para acessar e usar sensores

Ao projetar a implementação do sensor, siga as diretrizes discutidas nesta seção. Estas diretrizes são práticas recomendadas para quem usa o framework de sensores para acessar sensores e adquirir dados do sensor.

Coletar dados do sensor apenas no primeiro plano

Em dispositivos com o Android 9 (nível 28 da API) ou versões mais recentes, os apps em execução em segundo plano têm as seguintes restrições:

  • Os sensores que usam o modo de relatório contínuo, como acelerômetros e giroscópios, não recebem eventos.
  • Os sensores que usam os modos de relatório on-change ou one-shot não recebem eventos.

Dadas essas restrições, é melhor detectar eventos de sensores quando o app estiver em primeiro plano ou como parte de um serviço em primeiro plano.

Cancelar o registro de listeners de sensor

Cancele o registro de um listener de sensor quando terminar de usar o sensor ou quando a atividade dele for pausada. Se um listener de sensor for registrado e a atividade dele for pausada, o sensor vai continuar a adquirir dados e usar recursos da bateria, a menos que você cancele o registro do sensor. O código a seguir mostra como usar o método onPause() para cancelar o registro de um listener:

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);
}

Para mais informações, consulte unregisterListener(SensorEventListener).

Testar com o Android Emulator

O Android Emulator inclui um conjunto de controles de sensores virtuais que permitem testar sensores como acelerômetro, temperatura ambiente, magnetômetro, proximidade, luz e muito mais.

O emulador usa uma conexão com um dispositivo Android que executa o app SdkControllerSensor. Esse app está disponível apenas em dispositivos com o Android 4.0 (nível 14 da API) ou versões mais recentes. Se o dispositivo estiver executando o Android 4.0, ele precisará ter a Revisão 2 instalada. O app SdkControllerSensor monitora mudanças nos sensores do dispositivo e as transmite para o emulador. O emulador é transformado com base nos novos valores que recebe dos sensores do dispositivo.

O código-fonte do app SdkControllerSensor está disponível no seguinte local:

$ your-android-sdk-directory/tools/apps/SdkController

Para transferir dados entre o dispositivo e o emulador, siga estas etapas:

  1. Verifique se a depuração USB está ativada no dispositivo.
  2. Conecte o dispositivo à máquina de desenvolvimento por meio de um cabo USB.
  3. Abra o app SdkControllerSensor no dispositivo.
  4. No app, selecione os sensores que você quer emular.
  5. Execute este comando do adb:

  6. $ adb forward tcp:1968 tcp:1968
    
  7. Inicie o emulador. Agora você pode aplicar transformações ao emulador movendo o dispositivo.

Observação : se os movimentos que você faz no dispositivo físico não estiverem transformando o emulador, tente executar o comando adb da etapa 5 novamente.

Para mais informações, consulte o guia do Android Emulator.

Não bloqueie o método onSensorChanged()

Os dados do sensor podem mudar a uma taxa alta, o que significa que o sistema pode chamar o método onSensorChanged(SensorEvent) com bastante frequência. Como prática recomendada, faça o mínimo possível no método onSensorChanged(SensorEvent) para não o bloquear. Se o aplicativo exigir que você faça qualquer filtragem ou redução de dados do sensor, realize esse trabalho fora do método onSensorChanged(SensorEvent).

Evite usar métodos ou tipos de sensores obsoletos

Vários métodos e constantes tiveram o uso suspenso. Em particular, o tipo de sensor TYPE_ORIENTATION foi descontinuado. Para coletar dados de orientação, use o método getOrientation(). Da mesma forma, o tipo de sensor TYPE_TEMPERATURE foi descontinuado. Em dispositivos com o Android 4.0, use o tipo de sensor TYPE_AMBIENT_TEMPERATURE.

Verifique os sensores antes de usá-los

Sempre verifique se o sensor existe no dispositivo antes de tentar coletar dados dele. Não presuma que um sensor existe simplesmente porque ele é usado com frequência. Os fabricantes de dispositivos não são obrigados a fornecer sensores específicos nos dispositivos.

Tenha cuidado ao escolher os atrasos do sensor

Ao registrar um sensor com o método registerListener(), escolha uma taxa de entrega adequada para seu aplicativo ou caso de uso. Os sensores podem fornecer dados a taxas muito altas. Permitir que o sistema envie dados extras desnecessários desperdiça recursos do sistema e consome bateria.