Статьи

Android Things: понимание и написание драйверов

В предыдущей статье этой серии вы узнали о различных типах периферийных устройств ввода и вывода и подключений, которые можно использовать с Android Things. Затем вы можете расширить эти знания, чтобы написать новые классы, известные как драйверы, которые еще больше упрощают взаимодействие с периферийными устройствами. Эта статья будет посвящена типу драйверов, которые могут быть написаны для Android Things.

Драйверы пользовательского пространства позволяют разработчикам внедрять новое оборудование в среду Android, позволяя им взаимодействовать с уже установленными API-интерфейсами Android. Хотя вы можете напрямую связываться с устройствами с помощью стандартных API ввода / вывода, написание собственного драйвера позволит вашему приложению поддерживать различные аппаратные профили и напрямую работать с ОС Android. Кроме того, ваш код будет более структурированным и будет легко поддерживать повторное использование кода.

В этой статье вы узнаете о трех основных категориях драйверов: драйверы GPS, драйверы устройств ввода человека (HID) и драйверы датчиков.

Если вашему устройству требуется информация о местоположении, вы можете добавить устройство GPS в свое приложение. Зарегистрировав свое периферийное устройство GPS в UserDriverManager , вы сможете внедрить данные о местоположении вашего устройства в платформу Android, что позволит использовать его для служб определения местоположения Android. Это объединит данные GPS с WiFi и любым другим источником местоположения, чтобы обеспечить более точные результаты данных для вашего приложения.

Как правило, ваши модули GPS будут подключаться к устройству Android Things через соединение UART. Мы не будем углубляться в UART в этом учебнике, но вы можете узнать все о том, как он работает для периферийных устройств, в предыдущем учебнике этой серии .

GPS модуль и Adafruit Breakout Board

Для работы с вашим GPS-модулем вам необходимо создать новый Java-компонент для связи с вашим новым устройством. Мы назовем этот класс GpsDriverService .

Чтобы зарегистрировать свой GPS-модуль в платформе Android для данных о местоположении, сначала необходимо создать объект GpsDriver в вашем GpsDriverService . Этот объект может быть зарегистрирован с помощью UserDriverManager с помощью вызова registerGpsDriver() .

01
02
03
04
05
06
07
08
09
10
11
private GpsDriver mDriver;
 
@Override
public void onCreate() {
    super.onCreate();
 
    mDriver = new GpsDriver();
     
    UserDriverManager manager = UserDriverManager.getManager();
    manager.registerGpsDriver( mDriver );
}

Как только ваш GPS-модуль получит данные о местоположении и отправит их на ваше устройство Android Things через последовательное соединение UART, вам потребуется проанализировать их и добавить в объект Location .

Большинство модулей GPS возвращают данные о местоположении в формате NMEA , хотя анализ этих данных выходит за рамки данного руководства. Для вашего объекта Location есть четыре обязательных элемента данных: точность, время, широта и долгота. Вы также можете при желании указать высоту, направление и скорость (если устройство движется).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
private Location parseLocationFromString(String gpsData) {
    Location result = new Location(LocationManager.GPS_PROVIDER);
 
    //parse gpsData
 
    //required
    result.setAccuracy( getAccuracyFromGpsData( gpsData ) );
    result.setTime( getTimeFromGpsData( gpsData ) );
    result.setLatitude( getLatitudeFromGpsData( gpsData ) );
    result.setLongitude( getLongitudeFromGpsData( gpsData ) );
 
    //optional
    result.setAltitude( getAltitudeFromGpsData( gpsData ) );
    result.setBearing( getBearingFromGpsData( gpsData ) );
    result.setSpeed( getSpeedFromGpsData( gpsData ) );
 
    return result;
}

Заполнив объект Location , вы можете передать его в GpsDriver , вызвав reportLocation() .

1
2
Location location = parseLocationFromString( rawGpsData );
mDriver.reportLocation( location );

После того, как ваш компонент создан, вам нужно будет создать его экземпляр, начать чтение данных и прослушивать обновления в вашем приложении.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private LocationListener mLocationListener = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
        Log.v(«Test», «Location update: » + location);
    }
 
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        Log.e(«Test», «onstatuschanged»);
    }
 
    @Override
    public void onProviderEnabled(String provider) {
        Log.e(«Test», «onproviderenabled»);
    }
 
    @Override
    public void onProviderDisabled(String provider) {
        Log.e(«Test», «onproviderdisabled»);
    }
};
 
 
mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
 
// Create and start the GPS component
mGpsDriver = new GpsDriverService();
mGpsDriver.register();
 
// Register for location updates
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);

Когда ваше приложение будет завершено, вам нужно будет отменить регистрацию драйвера и удалить прослушиватель местоположения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Override
protected void onDestroy() {
    super.onDestroy();
 
    if (mGpsDriver != null) {
        mGpsDriver.unregister();
        mLocationManager.removeUpdates(mLocationListener);
 
        try {
            mGpsDriver.close();
        } catch (IOException e) {
        }
    }
}

Кроме того, вам необходимо убедиться, что ваше приложение имеет разрешение ACCESS_FINE_LOCATION . Вы можете найти подробную информацию о том, что приложение имеет необходимые разрешения в этом руководстве .

  • Android M
    Понимание разрешений в Android M

При первой работе с приложением, которое использует новое разрешение в Android Things, вам необходимо перезагрузить устройство после установки приложения, чтобы убедиться, что разрешение было предоставлено.

Платформа Android поставляется со встроенным конвейером для обработки ввода от пользовательских кнопок и событий движения, который используется для таких вещей, как мультимедийные кнопки, джойстики контроллера и нажатия клавиш клавиатуры. Создав InputDriver , вы можете связать свои собственные человеческие взаимодействия со стандартным конвейером ввода Android, чтобы ваше устройство могло правильно реагировать на ваших пользователей.

Для простоты мы рассмотрим только события ввода кнопок и как связать их с платформой Android, хотя события движения обрабатываются очень похожим образом. Как и в последней части этого урока, мы будем игнорировать более конкретные детали реализации устройств ввода и сосредоточимся на привязке полученных событий к платформе Android.

Когда событие кнопки происходит от кнопки, подключенной к вашему устройству Android Things, вы захотите записать это и отправить через конвейер Android.

Первое, что вам нужно сделать, это создать объект InputDriver в новом Service и инициализировать его. Драйвер можно инициализировать с помощью компоновщика, который принимает тип ввода, имя для ввода, версию и код клавиши, который представляет кнопка.

01
02
03
04
05
06
07
08
09
10
11
12
private InputDriver mDriver;
 
@Override
public void onCreate() {
    super.onCreate();
 
    mDriver = InputDriver.builder(InputDevice.SOURCE_CLASS_BUTTON)
            .setName(«ButtonInputDriver»)
            .setVersion(1)
            .setKeys(new int[] {KeyEvent.KEYCODE_SPACE})
            .build();
}

Как только ваш InputDriver инициализирован, вы можете зарегистрировать его в UserDriverManager с помощью вызова registerInputDriver() .

1
2
UserDriverManager manager = UserDriverManager.getManager();
manager.registerInputDriver(mDriver);

Как только вы зарегистрируете свой InputDriver , ваша служба драйверов может ожидать отправки ему событий из класса реализации вашей кнопки. Если ваша пользовательская кнопка нажата, вы можете уведомить службу и создать новый KeyEvent , который можно поместить в конвейер ввода Android с помощью метода emit(KeyEvent) . Этот метод возвращает значение true, если KeyEvent может быть отправлено в платформу Android, и значение false, если возникает ошибка.

1
2
3
4
5
6
7
if( buttonPressed ) {
    KeyEvent[] events = new KeyEvent[] {new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)};
     
    if (!mDriver.emit(events)) {
        //something went wrong
    }
}

Последнее, что вам нужно сделать, это InputDriver объекта UserDriverManager в UserDriverManager когда ваше приложение завершит работу.

1
2
3
4
5
6
7
@Override
public void onDestroy() {
    super.onDestroy();
 
    UserDriverManager manager = UserDriverManager.getManager();
    manager.unregisterInputDriver(mDriver);
}

Теперь, когда вы можете отправлять события ввода кнопок в конвейер ввода Android, самое время их прослушать. Здесь вся работа по перенаправлению ваших новых событий кнопок в платформу Android окупается. В Activity вашего приложения вам просто нужно добавить метод, когда KeyEvent не работает, и другой, когда KeyEvent не работает. В этих методах вы можете проверить код ключа KeyEvent и соответствующим образом обработать событие.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
     
    if( event.getKeyCode() == KeyEvent.KEYCODE_SPACE ) {
        //handle it
    }
 
    return true;
}
 
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    return true;
}

Некоторые из наиболее распространенных компонентов, которые вы будете использовать с платой Android Things, — это датчики. Поскольку Android имеет довольно надежную сенсорную структуру, имеет смысл только то, что мы хотели бы иметь возможность добавлять данные из наших внешних компонентов в этот конвейер.

Для начала вам нужно создать новый класс Java, который взаимодействует с вашим аппаратным датчиком. Этот класс должен будет расширить UserSensorDriver и реализовать метод read() . Кроме того, если ваш датчик поддерживает режимы пониженного энергопотребления или спящего режима, вы можете переопределить метод setEnabled() и действовать соответственно.

Следующий фрагмент кода представляет собой версию класса с заглушками, которая считывает данные через периферийный API ввода-вывода для получения значений данных X, Y и Z и возврата нового UserSensorReading . Если данные в данный момент недоступны, ваш класс может выдать новое IOException .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ExampleSensorComponent extends UserSensorDriver {
    float x, y, z;
 
    @Override
    public UserSensorReading read() throws IOException{
        try {
            // Read data from the sensor hardware and return it
            x = getXValueFromHardware();
            y = getYValueFromHardware();
            z = getZValueFromHardware();
 
            return new UserSensorReading(new float[]{x, y, z});
        } catch (Exception e) {
            // Error occurred reading the sensor hardware
            throw new IOException(«Unable to read sensor»);
        }
    }
 
    //Used if supporting low power/sleep mode
    @Override
    public void setEnabled(boolean enabled) throws IOException {
        super.setEnabled(enabled);
    }
}

Как только класс вашего компонента создан, вы можете создать новый Service который будет создавать его экземпляр, а также создать новый объект UserSensor и присоединить его к конвейеру датчика Android.

Есть два типа датчиков, которые вы можете добавить к этому конвейеру. Первыми являются предопределенные типы, такие как гироскопы, акселерометры, свет и близость, и они могут быть добавлены в конвейер следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private ExampleSensorComponent mExampleSensor;
private UserSensor mSensor;
 
private SensorManager mSensorManager;
 
@Override
public void onCreate() {
    super.onCreate();
 
    mExampleSensor = new ExampleSensorComponent();
 
    mSensor = UserSensor.builder()
            .setName(«ExampleSensorComponent»)
            .setVendor(«VendorName»)
            .setType(Sensor.TYPE_ACCELEROMETER)
            .setDriver(mExampleSensor)
            .build();
 
    UserDriverManager manager = UserDriverManager.getManager();
 
    // Register the new driver with the framework
    manager.registerSensor(mSensor);
 
    mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    mSensorManager.registerDynamicSensorCallback(new SensorCallback());
}
 
private class SensorCallback extends SensorManager.DynamicSensorCallback {
    @Override
    public void onDynamicSensorConnected(Sensor sensor) {
        //Sensor connected
        mSensorManager.registerListener(SensorDriverService.this, sensor,
                SensorManager.SENSOR_DELAY_NORMAL);
    }
 
    @Override
    public void onDynamicSensorDisconnected(Sensor sensor) {
        //Sensor disconnected
        mSensorManager.unregisterListener(SensorDriverService.this);
    }
}

Вы заметите, что SensorManager.DynamicSensorCallback используется в приведенном выше примере. Это уведомляет ваше приложение, когда датчик доступен для платформы, так как регистрация может занять некоторое время, чтобы платформа не пыталась считывать данные с недоступного устройства.

Adafruit датчик давления и температуры

Второй тип — пользовательский , который охватывает все, что еще не поддерживается в Android. Некоторые примеры включают уровни pH воды, скорость ветра, обнаружение движения или что-либо еще, что вы можете измерить с помощью нового оборудования.

setType() параметр setCustomType() , вы можете добавить имя вашего устройства и режим отчетов, чтобы контролировать, как оно будет срабатывать в конвейере.

1
2
3
4
5
6
7
8
9
mSensor = UserSensor.builder()
       .setName(«ExampleSensorComponent»)
       .setVendor(«VendorName»)
       .setType(Sensor.TYPE_ACCELEROMETER)
       .setCustomType(Sensor.TYPE_DEVICE_PRIVATE_BASE,
               «com.tutsplus.examplesensor»,
               Sensor.REPORTING_MODE_ON_CHANGE)
       .setDriver(mExampleSensor)
       .build();

Наконец, когда ваше приложение завершит работу, вам нужно будет UserDriverManager регистрацию нового компонента в UserDriverManager .

1
2
3
4
5
6
@Override
public void onDestroy() {
    super.onDestroy();
    UserDriverManager manager = UserDriverManager.getManager();
    manager.unregisterSensor(mSensor);
}

Из этого руководства вы узнали, как использовать компоненты, созданные с использованием API периферийного ввода-вывода, и связывать их с соответствующими платформами Android для использования в приложениях Android Things.

На данный момент в этой серии у вас есть все инструменты, необходимые для создания более глубоких проектов Android Things. Помимо написания собственных драйверов, вы можете найти написанные драйверы и внедрить их в свой собственный проект. Вы можете найти источник этих драйверов в репозитории Android Things GitHub или ознакомиться с некоторыми рабочими примерами с использованием этих драйверов .

В следующей статье этой серии мы пойдем на шаг дальше и создадим полноценный проект Android Things, который берет изображения с Raspberry Pi и загружает их в Firebase. А пока посмотрите другие наши учебники по разработке приложений для Android здесь, на Envato Tuts +!

  • Android SDK
    Что нового в Firebase? Обновления с саммита разработчиков Firebase
    Чике Мгбемена
  • Android SDK
    Firebase для Android: Хранение файлов
  • Android M
    Понимание разрешений в Android M
  • Android
    Введение в Android вещи