ウェアラブル向けアプリを作る 目次
原文
データレイヤーを使って呼び出しをする時、完了時に呼び出しの状態を受け取ることが出来て、リスナーにより終了時のあらゆる変更を受け取ることも出来ます。
データレイヤーを呼び出した状態を待つ
putDataItem()などのデータレイヤーAPIを呼び出すとPendingResultの応答を受け取ることがあります。PendingResultが作成されるとオペレーションはバックグラウンドでキューに入ります。あなたが特に何もしなければ、いずれこのオペレーションは静かに完了します。もし、オペレーションが完了した時に何かの処理を行いたいときはPendingResultで同期的あるいは非同期で結果ステータスを待機させます。非同期で待つ
UI thread上のコードではブロックされるデータレイヤーAPIの呼び出しを行ってはいけません。PendingResultオブジェクトにオペレーションが完了した時に発火されるコールバックを加えることで実行を非同期に行うことが出来ます。
pendingResult.setResultCallback(new ResultCallback<DataItemResult>() {
@Override
public void onResult(final DataItemResult result) {
if(result.getStatus().isSuccess()) {
Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
}
}
});
同期して待つ
バックグラウンドサービス(WearableListenerServiceの場合など)内の分離したhandlerスレッド内でコードが実行されているときはブロックで呼び出すことが出来ます。この場合、PendingResultオブジェクトのawait()を呼ぶことが出来ます。これによりリクエストが完了するまでブロックされ、応答オブジェクトが戻ってきます。
DataItemResult result = pendingResult.await();
if(result.getStatus().isSuccess()) {
Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
}
データレイヤーイベントをリッスンする
データレイヤーは同期して携帯端末とウェアラブルにまたがってデータを送信するため、多くの場合アイテムが作成されたとか、メッセージを受信したとか、ウェアラブルと携帯端末が接続された時などの重要なイベントをリッスンする必要があります。データレイヤーをリッスンするために、2つの選択肢があります。
・WearableListenerService.を継承したサービスを作る
・DataApi.DataListenerを実装したActivityを作る
どちらの選択肢でも、あなたの取り扱いたいデータイベントのコールバックをoverrideします。
WearableListenerServiceを使う
通常ウェアラブルと携帯端末の両方の中でこのサービスのインスタンスを作ります。もし、データイベントをどちらかのアプリで使用しないのであれば、このサービスをアプリ内で実装する必要はありません。例えば、携帯端末アプリでdata itemオブジェクトをsetしたりgetした時、ウェアラブルアプリでそれらの変更をリッスンしてUIを更新することが出来ます。ウェアラブルアプリがdata itemを一切更新をしないなら携帯端末アプリではウェアラブルアプリによるデータイベントを一切リッスンする必要がありません。
WearableListenerService:を使って下記のイベントをリッスン出来ます。
・onDataChanged() - data itemオブジェクトが作成、変更、削除された時に呼ばれます。接続されたどちらか片方でイベントが起きると、このコールバックは両サイドでトリガーされます。
・onMessageReceived() - メッセージが片方から送信されたら反対側でこのコールバックがトリガーされます。
・onPeerConnected()とonPeerDisconnected() - 携帯端末とウェアラブルが接続または切断された時に呼ばれます。片方の接続状態が変更されたら両サイドのコールバックがトリガーされます。
WearableListenerServiceを作るために
1.WearableListenerServiceを継承したクラスを作る
2.onDataChanged()などで、希望するイベントをリッスンする。
3.WearableListenerServiceについてシステムに通知するためにAndroid manifestのintent filterを記載する。
以下の例は簡単なWearableListenerService:を実装した例です。
public class DataLayerListenerService extends WearableListenerService {
private static final String TAG = "DataLayerSample";
private static final String START_ACTIVITY_PATH = "/start-activity";
private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";
@Override
public void onDataChanged(DataEventBuffer dataEvents) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onDataChanged: " + dataEvents);
}
final List events = FreezableUtils
.freezeIterable(dataEvents);
GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.build();
ConnectionResult connectionResult =
googleApiClient.blockingConnect(30, TimeUnit.SECONDS);
if (!connectionResult.isSuccess()) {
Log.e(TAG, "Failed to connect to GoogleApiClient.");
return;
}
// イベント毎にループを回しメッセージを送る
/ to the node that created the data item.
for (DataEvent event : events) {
Uri uri = event.getDataItem().getUri();
// URLのホスト値からnode idを取得する
String nodeId = uri.getHost();
// URIのbyteをメッセージのデータとしてセットする。
byte[] payload = uri.toString().getBytes();
// RPCを送る
Wearable.MessageApi.sendMessage(googleApiClient, nodeId,
DATA_ITEM_RECEIVED_PATH, payload);
}
}
}
これは対応したAndroid manifestファイル内のIntent filterです。
訳注:このIntent filterは非効率なため現在では非推奨となっています。
新しいom.google.android.gms.wearable.DATA_CHANGEDを使用してください。
<service android:name=".DataLayerListenerService">
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
データレイヤーのためのPermission
データレイヤーイベントのコールバックをアプリケーションに届けるために、Google Play serviceはWearableListenerServiceをバインドし、コールバックをIPCを通じて呼びます。この結果コールバックは呼び出し元プロセスのPermissionを継承します。アプリはあなたのアプリプロセスのIDではなく呼び元のプロセスのIDでコールバックが動いているためコールバック内で権限が必要な処理を行おうとするとセキュリティチェックが失敗します。
これを防ぐには、IPCの境界線を超えたあとにIDをリセットするためにclearCallingIdentity()を呼び権限が必要な処理が終わったら、IDを戻すためにrestoreCallingIdentity()を呼びます。
long token = Binder.clearCallingIdentity();
try {
performOperationRequiringPermissions();
} finally {
Binder.restoreCallingIdentity(token);
}
Listener Acitivityを使う
もし、アプリが長時間動くサービスですべてのデータ変更を取り扱う必要がなく、ユーザーがアプリと対話している時だけデータレイヤーイベントを扱う必要があるのであれば、以下のinterfaceを1つ以上実装することでActivity内でイベントをリッスン出来ます。・DataApi.DataListener
・MessageApi.MessageListener
・NodeApi.NodeListener
データイベントをリッスンするActivityを作るには
1.希望するインターフェイスを実装する
2.onCreate(Bundle)の中でdata layer APIと働くGoogleApiClientのインスタンスを作る
3.onStart()内でconnect()を呼んでクライアントとGoogle Play serviceを接続する
4.Google Play serviceとの接続が確立したら、システムによってonConnected()を呼びだされるので。Activityで必要なデータレイヤーイベントをGoogle Play serviceに伝えるためにDataApi.addListener()やMessageApi.addListener()やNodeApi.addListener()を呼ぶ。
5.onStop()内でDataApi.removeListener()やMessageApi.removeListener()やNodeApi.removeListener()を呼んで全てのリスナーをアンレジストする。
6.実装したインターフェイスのonDataChanged()やonMessageReceived()やonPeerConnected()そしてonPeerDisconnected()を実装する。
これはDataApi.DataListenerを実装した例です。
public class MainActivity extends Activity implements
DataApi.DataListener, ConnectionCallbacks, OnConnectionFailedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
@Override
protected void onStart() {
super.onStart();
if (!mResolvingError) {
mGoogleApiClient.connect();
}
}
@Override
public void onConnected(Bundle connectionHint) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Connected to Google Api Service");
}
Wearable.DataApi.addListener(mGoogleApiClient, this);
}
@Override
protected void onStop() {
if (null != mGoogleApiClient && mGoogleApiClient.isConnected()) {
Wearable.DataApi.removeListener(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
super.onStop();
}
@Override
public void onDataChanged(DataEventBuffer dataEvents) {
for (DataEvent event : dataEvents) {
if (event.getType() == DataEvent.TYPE_DELETED) {
Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
} else if (event.getType() == DataEvent.TYPE_CHANGED) {
Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
}
}
}
Except as noted, this content is licensed under Creative Commons Attribution 2.5. For details and restrictions, see the Content License.