Wearと本体でデータを共有するにはDataItemを使う。

DataItemはkey-valueストアで、1キーに100KBまで保存できる。文字列・数値だけでなく画像などのバイナリデータも保存できて、Bluetoothの帯域を適当に考慮して同期してくれるらしい。
具体的な処理は隠蔽されているがデータはGoogleのサーバーにも保存されているっぽい。
そのためGoogle API Clientとインターネットへの接続が必要。

key-valueは wear://<node_id>/<path> のようなURIの下に保存されるが、node_idは普通意識しないのでpathだけ指定して使う。


保存期限

いつまでデータが生きているのかは、ドキュメントに書かれていないのでわからない。
期限を設定するプロパティが無い事と、DataItemに書き込んでからスマホとWearを両方とも再起動してもデータは残っていたことから、まあずっと残ってるんじゃないかと思う。
SharedPreferenceみたいにも使えそう。

片方が生きてない時にDataItemに書き込んで、もう片方だけ起動したら読めるか?を試そうとしたけどWearはそもそもスマホがないとネットワーク接続できなかったから試せなかった。


使い方

“/testapp”というpathの中で、名前とウェブサイトをWearと本体間で共有する例


書き込み

GoogleApiClientを接続してからkey-valueを保存する
PutDataMapRequest mapReq = PutDataMapRequest.create("/testapp");
mapReq.getDataMap().putString("name", "shokai");
mapReq.getDataMap().putString("url", "http://shokai.org");
Wearable.DataApi.putDataItem(mGoogleApiClient, mapReq.asPutDataRequest());

読み出し(イベント)

Wearable.DataApiにDataApi.DataListenerを登録しておけばデータの変更が通知されてくる。

// Activity自身をlistenerとして登録
Wearable.DataApi.addListener(mGoogleApiClient, this);
// 解除
Wearable.DataApi.removeListener(mGoogleApiClient, this);

public class MainActivity extends Activity implements DataApi.DataListener {

// (略)

@Override
public void onDataChanged(DataEventBuffer dataEvents) {
for (DataEvent event : dataEvents) {
if (event.getType() == DataEvent.TYPE_CHANGED) {
DataItem item = event.getDataItem();
if (item.getUri().getPath().equals("/testapp")) {
DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
String name = dataMap.getString("name"); // "shokai"
String url = dataMap.getString("url"); // "http://shokai.org"
}
} else if (event.getType() == DataEvent.TYPE_DELETED) {
// 削除イベント
}
}
}

1回書き込んだだけでも、なぜか連続で2回イベントが来る事があった。1回目は古いデータで、2回目は更新されたデータがくる。


読み出し

イベントではなく、現在の値をこっちから読みに行く方法は
Android WearのData Layer APIを試してみた – bati11's diary
に書いてあった。GoogleAPIClientを接続してから
Wearable.DataApi.getDataItems(googleClient)
.setResultCallback(new ResultCallback() {
@Override
public void onResult(DataItemBuffer dataItems) {
String name = null;
String url = null;
for(DataItem dataItem : dataItems){
if(dataItem.getUri().getPath().equals("/testapp")) {
DataMap dataMap = DataMap.fromByteArray(dataItem.getData());
name = dataMap.getString("name");
url = dataMap.getString("url");
}
}
if(name != null && url != null) {
Log.i("userinfo", "found");
}
else{
Log.i("userinfo", "not found");
}
dataItems.release(); // releaseしないとリークするという警告がでる
}
});
この方法で他のアプリケーションのデータも読めてしまうかと思ったけど、読めなかった。よかった。


削除

DataMapまるごとしか削除できない。保存する時はpath指定なのに削除はURI指定しなければならない。

Uriのnode_idは省略できるので、/testappに保存してるDataMapはwear:/testappを指定したら削除できた。
削除するとonDataChangedイベントのTYPE_DELETEDが発火する。

Wearable.DataApi.deleteDataItems(mGoogleApiClient, Uri.parse("wear:/testapp"))
.setResultCallback(new ResultCallback() {
@Override
public void onResult(DataApi.DeleteDataItemsResult deleteDataItemsResult) {
Log.i("deleteDataItems", deleteDataItemsResult.getNumDeleted() + "個削除した");
}
});
}

DataItemにはnullも書き込めるので、DataMapまるごと削除したくない時はnull埋めでもいいかもしれない。