Android ThingsのUser-Space Driversがいまひとつぴんとこなかったので、サンプルのsample-buttonを、ドキュメントのコード例のようにServiceで実装しながらまとめたメモ。
User Driverとして実装することの利点
ほとんどのアプリは、Peripheral I/Oを使って直接H/Wとやりとりするだけで足りるので、User Driverとして局所化し、Android Frameworkを介するまでもない。しかし、可搬性・再利用性・統合性を高めたかったらUser Driverとして実装したほうがよい、ということ。
User Driverタイプ
User Driver APIが対象としているのは、GPS、キーボードなどHuman Interface Devices(HID)、センサーの3種類の入力デバイス。drivers-samplesの中のSegment display sampleが使用しているht16k33ディスプレイドライバなどの出力デバイスドライバを広義に(あるいは混同して)User Driverと呼ぶことはあるかもしれないが、少なくともAPIの対象ではない。
com.google.android.things.userdriverパッケージクラス |
---|
GpsDriver |
InputDriver |
InputDriver.Builder |
UserDriverManager |
UserSensor |
UserSensor.Builder |
UserSensorDriver |
UserSensorReading |
ただし、出力デバイスも入力デバイス同様にServiceなどに隠蔽して疎結合とし、可搬性を高めることは可能であり、実際にサンプルのLEDドライバをService化してみた。
実装例
GPS/HID/センサーともにパターンは同じ。尚、ドキュメントのコード例はServiceを使用しているが、サンプルのcontrib-driversでは使用しておらず、必須というわけではない。前述の通りServiceを使用したほうが若干結合度が弱いと思うが、このあたりはケースバイケースで使い分ければよいだろう。
User Driver側
といっても、往々にしてアプリと同梱なので、アプリ下位側とでもいうべきか。
- 開始時にドライバを生成し、UserDriverManagerに登録する。
- GPSは、GpsDriverを生成し登録する。
private GpsDriver mDriver;
...
mDriver = new GpsDriver();
UserDriverManager.getManager().registerGpsDriver(mDriver);
- HIDは、ビルダーを使ってInputDriverを生成し、登録する。
private InputDriver mDriver;
...
mDriver = InputDriver.builder(InputDevice.SOURCE_CLASS_BUTTON)
.setName(DRIVER_NAME)
.setVersion(DRIVER_VERSION)
.setKeys(new int[] {KEY_CODE})
.build();
UserDriverManager.getManager().registerInputDriver(mDriver);
- センサーは、UserSensorDriverの継承クラスインスタンスを生成し、ビルダーを使ったUserSensor生成時に渡し、UserSensorインスタンスを登録する。
private UserSensorDriver mDriver = new UserSensorDriver() {};
private UserSensor mSensor;
...
mSensor = UserSensor.builder()
.setName("GroveAccelerometer")
.setVendor("Seeed")
.setType(Sensor.TYPE_ACCELEROMETER)
.setDriver(mDriver)
.build();
UserDriverManager.getManager().registerSensor(mSensor);
- トリガー発生時もしくは定期的にH/Wイベントを注入する。
- GPSは、フレーム周期ごとにLocationイベントを送る。
mDriver.reportLocation(mLastKnownLocation);
- HIDは、監視しているポートの立ち上がり(立ち下がり)を検知し、イベントを発する。
int action = pressed ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
KeyEvent[] events = new KeyEvent[] {new KeyEvent(action, KEY_CODE)};
mDriver.emit(events);
- センサーは、UserSensorDriver#readがコールバックされたときに、センサーデバイスからデータを読み込んで値を返す。
UserSensorDriver mDriver = new UserSensorDriver() {
// Sensor data values
float x, y, z;
@Override
public UserSensorReading read() {
try {
// ...read the sensor hardware...
// Return a new reading
return new UserSensorReading(new float[]{x, y, z});
} (catch Exception e) {
// Error occurred reading the sensor hardware
throw new IOException("Unable to read sensor");
}
}
};
- 終了時にUserDriverManagerの登録を解除する。
- GPS
UserDriverManager.getManager().unregisterGpsDriver(mDriver);
- HID
UserDriverManager.getManager().unregisterInputDriver(mDriver);
- センサー
UserDriverManager.getManager().unregisterSensor(mSensor);
アプリ側
- 開始時にサービスを起動する。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(new Intent(this, YourDriverService.class));
}
- コールバックでイベントを受信する。
- GPSは、LocationListenerで受信する。
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
0, 0, new LocationListener() {
@Override
public void onLocationChanged(Location location) { }
@Override
public void onStatusChanged(String provider, int status, Bundle extras) { }
@Override
public void onProviderEnabled(String provider) { }
@Override
public void onProviderDisabled(String provider) { }
});
- HIDは、当該メソッドで受信する。以下はキーの場合。
public boolean onKeyDown(int keyCode, KeyEvent event) {
return super.onKeyDown(keyCode, event);
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
return super.onKeyUp(keyCode, event);
}
- センサーは、SensorEventListenerで受信する。
SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensorManager.registerDynamicSensorCallback(new SensorManager.DynamicSensorCallback() {
@Override
public void onDynamicSensorConnected(Sensor sensor) {
if (sensor.getType() == Sensor.TYPE_AMBIENT_TEMPERATURE) {
sensorManager.registerListener(new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) { }
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) { }
}, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
});
- 終了時にサービスを停止する。
protected void onDestroy(){
super.onDestroy();
stopService(new Intent(this, YourDriverService.class));
}
Service化したソースはここにあります。
https://github.com/tatsu/sample-button