Android Wear 进阶 - 4 发送和同步数据 <Sending and Syncing Data>


首先要注意的是Layer 是层的意思。Layout 是布局的意思。所以Data Layer 是数据层的意思。

第一部分:总述:

手表端有4种类型和数据层相关的:

Data Item(数据项):限制100KB的数据,主要数据同步,一个数据项提供了 数据存储,这些数据会自动的在手持端和可穿戴端同步

Message(消息,信息):RPC模式,单程请求,可以用来启动activity,例如手表端开启手机端的acitivity,例如音乐播放的按钮

MessageAPI类可以发送消息,并且对于RPC非常好,例如可以从可穿戴设备控制手持端设备的媒体播放,或者启动一个intent,。
消息同时对于单程(单行)的请求或者是请求回复通信模式也是很好的。
如果手持端和可穿戴连接了。系统将会排列好消息去传送,并且返回一个成功的code
如果手持端和可穿戴没有连接的话,一个error将会返回。
一个成功的结果码并不代表消息已经被传递成功了,因为设备可能在收到结果码后断开连接。

Asset(资产):可以用来传送图片

资产对象是为了传送二进制的对象例如images。 你将资产放到数据项(Data Item)上面,系统会自动的为你进行转换,保持蓝牙的带宽,通过缓存大的资产去避免重复传送。

Channel(通道,频道):可以用来传送特大的,例如电影,音乐,主要为了大的数据


你可以使用频道API 类去转换大的数据项目,例如音乐或者电影的文件,从一个手持端的设备到可穿戴的设备上面,
频道的API 专门为了数据转换 有一下几个优点:
1:传送大的数据文件,在两个或或者更多连接的设备,不需要当使用Asset 资产对象(这些对象被贴到数据项对象上面)自动同步。
这个频道API 可以节省磁盘空间,不像DataApi类,DataApi 类需要在同步连接设备的时候创建一个assets拷贝文件在本地的设备上面。
2:可以可靠的传送一个文件,这个文件的很大的文件,不可以通过MessageApi类来进行传送
3:转换流动的数据,例如音乐从网络的服务器或者声音数据从麦克风。

警告: 因为这些API 是为了手持端和可穿戴的通讯进行设计,这些API 是你唯一可以使用来建立这些设备的通讯的。例如不要试着打开底层的sockets去
建立通讯的信道。Android Wear 支持多个可穿戴设备连接到一个手持端的设备,例如当用户保存了一个记事本到了手持端,这个会自动的出现在用户的两
个可穿戴的设备上面,为了让设备之间进行同步,谷歌服务器 主持了一个云端的node在网络设备上面,系统同步数据到连接的设备上面,云端的node,和
可穿戴设备链接到云端node的(同步哦wifi)

一种服务:

WearableListenerService (for services),可穿戴的监听服务。
扩展的可穿戴监听服务,让你可以监听重要的数据层事件在一个服务里面。
系统管理可穿戴监听服务的生命周期。当需要传送数据或者消息的时候进行绑定到服务,当不需要做任何工作的时候进行解绑的操作

一种监听事件:

DataListener (for foreground activities)数据监听,为了前台的activities
在一个activity 中实现数据监听会让你监听重要的数据层事件,当这个activity 是在前台的的时候。
使用这个而不是可穿戴监听服务 WearableListenerService会让你监听到变换,这个变化是正当用户使用你的app的时候

-------------------------------------------------------------------------------------------------------

第一部分:访问可穿戴设备的数据层。Accessing the Wearable Data Layer

-------------------------------------------------------------------------------------------------------

GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(new ConnectionCallbacks() {
                @Override
                public void onConnected(Bundle connectionHint) {
                    Log.d(TAG, "onConnected: " + connectionHint);
                    // Now you can use the Data Layer API
                }
                @Override
                public void onConnectionSuspended(int cause) {
                    Log.d(TAG, "onConnectionSuspended: " + cause);
                }
        })
        .addOnConnectionFailedListener(new OnConnectionFailedListener() {
                @Override
                public void onConnectionFailed(ConnectionResult result) {
                    Log.d(TAG, "onConnectionFailed: " + result);
                }
            })
        // Request access only to the Wearable API
        .addApi(Wearable.API)
        .build();

Important:使用GoogleApiClient 来访问,因为有可能手表并没有连接上,所以最好使用addApiIfAvailable() 而不是addApi()。




-------------------------------------------------------------------------------------------------------

第二部分:同步数据项目:Syncing Data Iterms

-------------------------------------------------------------------------------------------------------


数据项目可以用来同步手机端和可穿戴设备之间的通讯。
需要两个东西一个是是 Payload 一个是 path
Payload就是一个字节数组,你可以设置任何你想要的数据,允许你去使用自己的序列化和反序列化。这个payload的大小限制在100KB.
Payload的意思就是有效载荷

一般不直接使用DataItem ,而是使用PutDataRequest 对象,

1:创建一个PutDataRequest对象,分配一个唯一标识字符路劲path给数据item。

2:使用setData 来设置payload

3:使用DataApi.putDataItem()来请求系统创建数据项DataItem。

4: 当请求数据项的时候,系统会将实现了DataItem 接口的对象返回。

然后,对于setData()来说,我们一般使用data map 来替代它,这个data map在一个可以轻松使用的Bundle类似的接口里面 将数据项释放出来。


使用DataMap 来同步数据。

如果使用DataMap 类,这个方法可以使我们以android bundle 的方式来使用数据项,所以对象序列化和反序列化都已经为我们做好了,我们可以使用key-value的形式来使用数据项了。



为了使用数据图:(data map)
1:创建PutDataMapRequest 对象,设置数据项的路径path,
注意了:path的String 是数据项的一个唯一的标识,允许我们通过另外一端访问它(也就是说可以在手机端访问手表端的数据,也允许手表端访问手机端
的数据)这个path 必须以反斜杠/开始,如果你使用结构化的数据在你的应用里面,你应该创建一个path scheme 来匹配到数据的结构上面。
2:调用PutDatMapRequest.getDataMap() 来获取一个data map 这样我们就可以设置values
3:使用put。。。()方法来设置需要设置的data map的值,例如putString()
4:调用PutDataMapRequest.asPutDataRequest() 来获取PutDataReqest对象
5:调用DataApi.putDataItem()方法来请求系统去创建数据项data item
注意了:如果手机端和手表端断开连接了,数据将会缓存起来当再次连接的时候会同步的。
The increaseCounter() method in the following example shows how to create a data map and put data in it:

public class MainActivity extends Activity implements
        DataApi.DataListener,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    private static final String COUNT_KEY = "com.example.key.count";

    private GoogleApiClient mGoogleApiClient;
    private int count = 0;

    ...

    // Create a data map and put data in it
    private void increaseCounter() {
        PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/count");
        putDataMapReq.getDataMap().putInt(COUNT_KEY, count++);
        PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
        PendingResult<DataApi.DataItemResult> pendingResult =
                Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq);
    }

    ...
}


关于PendingResult 对象,请查看Wait for the Status of Data Layer Calls.
这个Wait for the Status of Data Layer Calls 在Handling Data Layer Events这一节里面


监听数据项事件:
如果数据层连接的一边改变了一个数据项,你可能想要通知连接的另一端。你可以实现这个通过监听这个数据项事件


public class MainActivity extends Activity implements
        DataApi.DataListener,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    private static final String COUNT_KEY = "com.example.key.count";

    private GoogleApiClient mGoogleApiClient;
    private int count = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnected(Bundle bundle) {
        Wearable.DataApi.addListener(mGoogleApiClient, this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        Wearable.DataApi.removeListener(mGoogleApiClient, this);
        mGoogleApiClient.disconnect();
    }

    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {
        for (DataEvent event : dataEvents) {
            if (event.getType() == DataEvent.TYPE_CHANGED) {
                // DataItem changed
                DataItem item = event.getDataItem();
                if (item.getUri().getPath().compareTo("/count") == 0) {
                    DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
                    updateCount(dataMap.getInt(COUNT_KEY));
                }
            } else if (event.getType() == DataEvent.TYPE_DELETED) {
                // DataItem deleted
            }
        }
    }

    // Our method to update the count
    private void updateCount(int c) { ... }

    ...
}

------------------------------------------------------------------------------------------------------------------------

第三部分:传送资产 Transferring Assets

------------------------------------------------------------------------------------------------------------------------

传送资产,就是传送大的二进制数据
为了通过蓝牙发送大的blob类型的二进制数据,例如图片,将Asset添加到数据项data item 上面,将数据项放到重复的数据存储。

assets 资产会自动的处理缓存数据来防止重复的传送和保护蓝牙带宽。

一个常用的模式是手机端下载了一个图片,将这个图片缩放到一个合适尺度可以放到手表端。
同时将这个传送到手表端作为一个资产asset。下面的例子演示了这个模式
注意:虽然data item 的大小限制在100kb,但是资产asset可以想多大就多大,但是传送大的资产会在很多情况下影响用户的用户体验,
所以测试你的应用保证他们运行很好如果你要传送大的数据资产asset。


传送一个资产asset:

创建一个资产使用create...()的方法在Asset类里面,这里我们将一个bitmap的转换成字节流,然后调用createFromBytes()方法去创建asset


private static Asset createAssetFromBitmap(Bitmap bitmap) {
    final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream);
    return Asset.createFromBytes(byteStream.toByteArray());
}

当你有一个资产asset的时候,在DataMap 或者PutDataRequest里面将这个贴到数据项data item 上面使用putAsset()的方法,然后再使用putDataItem的方法将数据项放到数据存储里面。
注意下面的createAssetFromBitmap就是上面的方法。

Using PutDataRequest

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
Asset asset = createAssetFromBitmap(bitmap);
PutDataRequest request = PutDataRequest.create("/image");
request.putAsset("profileImage", asset);
Wearable.DataApi.putDataItem(mGoogleApiClient, request);

Using PutDataMapRequest

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
Asset asset = createAssetFromBitmap(bitmap);
PutDataMapRequest dataMap = PutDataMapRequest.create("/image");
dataMap.getDataMap().putAsset("profileImage", asset)
PutDataRequest request = dataMap.asPutDataRequest();
PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi
        .putDataItem(mGoogleApiClient, request);

接收 资产

当一个资产创建了,你可能想要从连接的另外一端阅读或者提取这个,这里有一个例子,解释怎么实现资产变换后的检测和提取的回调。


@Override
public void onDataChanged(DataEventBuffer dataEvents) {
  for (DataEvent event : dataEvents) {
    if (event.getType() == DataEvent.TYPE_CHANGED &&
        event.getDataItem().getUri().getPath().equals("/image")) {
      DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
      Asset profileAsset = dataMapItem.getDataMap().getAsset("profileImage");
      Bitmap bitmap = loadBitmapFromAsset(profileAsset);
      // Do something with the bitmap
    }
  }
}

public Bitmap loadBitmapFromAsset(Asset asset) {
    if (asset == null) {
        throw new IllegalArgumentException("Asset must be non-null");
    }
    ConnectionResult result =
           mGoogleApiClient.blockingConnect(TIMEOUT_MS, TimeUnit.MILLISECONDS);
    if (!result.isSuccess()) {
        return null;
    }
    // convert asset into a file descriptor and block until it's ready
    InputStream assetInputStream = Wearable.DataApi.getFdForAsset(
            mGoogleApiClient, asset).await().getInputStream();
            mGoogleApiClient.disconnect();

    if (assetInputStream == null) {
        Log.w(TAG, "Requested an unknown Asset.");
        return null;
    }
    // decode the stream into a bitmap
    return BitmapFactory.decodeStream(assetInputStream);
}

--------------------------------------------------------------------------------------------------------

第四部分:发送和接受消息Message - Sending and Receiving Message

--------------------------------------------------------------------------------------------------------

你发送message 使用MessageApi 然后将这个贴到items 上满,和前面一样也是payload 有效载荷和path
不同于数据项data item的是,手持端和可穿戴端是没有同步的。消息是一种单方向的通讯这种模式适合与RPC,例如发送一个消息到手持端去启动一个activity

多个可穿戴设备可以连接到一个用户的手机端。每一个可穿戴设备被认为是一个节点 Node。多个设备连接后,你需要决定哪一个设备来接受信息,例如,
在一个语音转换的app,你应该将信息发给可以处理的节点例如。
注意:在Google Play Service 7.3.0之前,只能一次有一个可穿戴设备可以连接到手机端。你可能需要更新现在的代码去将多个设备连接的特性考虑进去。
如果你没有实现这些,那么你的信息可能不会传送到你想要的设备上面。


发送消息:
一个可穿戴设备可以提供一些功能例如语音转换。用户可以对着手表的麦克风说话,然后将转换的东西保存为记事本。因为手表设备本质上来说没有这种这种声音转化的activity,应用需要将这个工作交给一个可以处理的连接的设备来处理。
下面的部分展示了如何通知所有的设备节点去处理acitivity 请求,发现节点可以处理请求,发送信息到这些节点。

通知/告知能力:Advertise capabilities
为了在一个手表端设备上面启动一个手机端的activity,使用MessageApi 类来发送请求。既然很多可穿戴设备可以链接到手机端,那么手表应用需要决定哪一个连接的节点适合启动acitivity。在你的手机端app,通知那个节点运行可以提供特殊能力的。
为了通知你的手机端app的能力。
1:创建XML配置文件在/res/values/文件夹,然后命名为wear.xml
2:wear.xml里面添加一个资源名为android_wear_capabilities
3:定义设备提供的能力
注意了能力是你定义自定义的strings并且必须唯一。
下面的例子展示了怎么添加一个能力名为voice_transcription 到wear.xml

wear.xml:

<resources>
    <string-array name="android_wear_capabilities">
        <item>voice_transcription</item>
    </string-array>
</resources>


通过需求的能力来解析节点:

原则上来说,你可以探测到能力节点通过调用CapabilityApi.getCapability()方法,下面的例子解释了怎么手动的解析可到达的节点的结果。通过

voice_transcription能力。这个voice_transcription就是上面定义的。


voice_transcription capability:

private static final String
        VOICE_TRANSCRIPTION_CAPABILITY_NAME = "voice_transcription";

private GoogleApiClient mGoogleApiClient;

...

private void setupVoiceTranscription() {
    CapabilityApi.GetCapabilityResult result =
            Wearable.CapabilityApi.getCapability(
                    mGoogleApiClient, VOICE_TRANSCRIPTION_CAPABILITY_NAME,
                    CapabilityApi.FILTER_REACHABLE).await();

    updateTranscriptionCapability(result.getCapability());
}
上面的updateTranscriptionCapability方法在下面会解释。


为了检测到能力节点当他们连接到手表端的时候,注册一个监听CapabilityApi.CapabilityListener()给你的GoogleApiClient的实例。
下面的例子展示了怎么注册监听和从可到达的有voice_transcription能力的节点上面返回的值。你发送message 使用MessageApi 然后将这个贴到items 上满和前面一样也是payload 有效载荷和path
不同于数据项data item的是,手持端和可穿戴端是没有同步的。消息是一种单方向的通讯这种模式适合与RPC,例如发送一个消息到手持端去启动一个

voice_transcription capability:

private void setupVoiceTranscription() {
    ...

    CapabilityApi.CapabilityListener capabilityListener =
            new CapabilityApi.CapabilityListener() {
                @Override
                public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
                    updateTranscriptionCapability(capabilityInfo);
                }
            };

    Wearable.CapabilityApi.addCapabilityListener(
            mGoogleApiClient,
            capabilityListener,
            VOICE_TRANSCRIPTION_CAPABILITY_NAME);
}

Note:注意:如果你创建了一个服务继承了WearableListenerService去检测能力变化,你可能想要重写onConnectedNodes()的方法去监听细粒度的连接细节,例如当手表设备从Wi-Fi切换到蓝牙连接到手表。例如实现,可以查看查找我的手机里面的DisconnectListennerService 类,(这个可以用来宝宝防走丢了)。更多的信息请查看下面一节Listen for Data Layer Events

fine-grained adj.有细密纹理的,细粒的

检测到能力节点然后决定了发送消息到那里之后,你应该选取一个节点接近近距离的到你的手表设备,这样可以让你的数据传送的路劲尽可能的短。一个nearby靠近的节点定义为一个可以直接连接的设备。为了决定这个节点是否是nearby的,调用Node.isNearBy方法。
下面的例子展示你可能决定的最好节点去使用。

private String transcriptionNodeId = null;

private void updateTranscriptionCapability(CapabilityInfo capabilityInfo) {
    Set<Node> connectedNodes = capabilityInfo.getNodes();

    transcriptionNodeId = pickBestNodeId(connectedNodes);
}

private String pickBestNodeId(Set<Node> nodes) {
    String bestNodeId = null;
    // Find a nearby node or pick one arbitrarily
    for (Node node : nodes) {
        if (node.isNearby()) {
            return node.getId();
         }
         bestNodeId = node.getId();
    }
    return bestNodeId;
}

传送信息:

一旦你确定了最好的节点使用了,使用MessageApi类来发送消息。
下面的例子显示怎么从手表端发送一个消息到有传送能力的节点。检验这个节点是否可以使用在你想要发送信息之前。
这个调用是同步的可以打断的进程,知道系统排列好信息发送。
注意了:一个成功的返回值未必能够保证传送了信息。如果i的应用需要保证数据的可靠新,使用DataItem数据项对象或者ChannelApi类来发送数据在不同的设备之间。

Transcription 抄写,翻译。这里的transcriptionNodeId就是上面的transcriptionNodeId。


public static final String VOICE_TRANSCRIPTION_MESSAGE_PATH = "/voice_transcription";

private void requestTranscription(byte[] voiceData) {
    if (transcriptionNodeId != null) {
        Wearable.MessageApi.sendMessage(googleApiClient, transcriptionNodeId,
            VOICE_TRANSCRIPTION_MESSAGE_PATH, voiceData).setResultCallback(
                  new ResultCallback() {
                      @Override
                      public void onResult(SendMessageResult sendMessageResult) {
                          if (!sendMessageResult.getStatus().isSuccess()) {
                              // Failed to send message
                          }
                      }
                  }
            );
    } else {
        // Unable to retrieve node with transcription capability
    }
}


你也可以将这个广播给所有的连接的节点。得到所有的节点的方法如下:

private Collection<String> getNodes() {
    HashSet <String>results = new HashSet<String>();
    NodeApi.GetConnectedNodesResult nodes =
            Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
    for (Node node : nodes.getNodes()) {
        results.add(node.getId());
    }
    return results;
}

接收信息:
为了被通知得到了信息,实现MessageListener接口,这个接口提供了一个监听器给信息事件。然后通过MessageApi.addListener()的方法来注册舰艇时间
下面的例子解释了怎么实现监听器去查看VOICE_TRANSCRIPTION_MESSAGE_PATH。如果这个条件为true。那么启动一个activity去处理语音数据。

@Override
public void onMessageReceived(MessageEvent messageEvent) {
    if (messageEvent.getPath().equals(VOICE_TRANSCRIPTION_MESSAGE_PATH)) {
        Intent startIntent = new Intent(this, MainActivity.class);
        startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startIntent.putExtra("VOICE_DATA", messageEvent.getData());
        startActivity(startIntent);
    }
}



------------------

第五部分:处理数据层事件 Handling Data Layer Event

-----------------

当你使用调用数据层的api的时候,你可以接收到调用状态的变化当他完成了或者是有任何变化的时候,


等待数据层的调用状态:

你将会注意到数据层api的调用有的时候会返回一个PendingResult,例如当putDataItem()方法。当这个PendingResult被创建了,操作将会在后台排队。
如果你这个之后没有做任何,那么这个操作最终会静默完成。然而,你一般是想要对结果做一些东西的当操作完成后,所以PendingResult允许i等待结果状态,要不是同步的要不是异步的。

异步调用:

如果你的代码运行在主UI进程中,不要使用阻塞调用数据层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());
        }
    }
});
同步调用:
如果你的代码运行在一个后台服务的分离的handler线程中(例如在一个WearableListenerService中),最好调用阻塞。

这样的话你可以调用PendingResult对象的await()的方法,当请求完成并且返回一个Result对象的时候阻塞。


DataItemResult result = pendingResult.await();
if(result.getStatus().isSuccess()) {
    Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
}


监听数据层事件:
因为数据层同步和发送数据通过手机端或者手表端,你通常希望监听重要的事件,例如当数据项被创建了,信息收到了,又或者手表端和手机端连接了。
有两个选择来监听数据层事件:
1:创建一个继承了WearableListenerService的服务
2:创建activity 是实现了DataApi.DataListener 的activity
这两种方法,你重写了数据事件的回调方法为了你感兴趣的处理。


第一种方法:使用继承了WeableListenerService的服务。

你需要在你的手表端和手机端都创建这个服务,如果你不对数据事件感兴趣,可以不实现这个服务
例如,你可以有一个手机端的app 他设置和得到数据项对象,手表端的app监听这些更新去更新自己的UI。手表端不用更新任何数据项目,所有手机端app 


不用设置监听任何手表端更新的数据事件。
你可以监听下面的事件通过WearableListenerService
onDataChanged() 当data item 数据项创建了,变化了,或者被删除了的时候调用。一个事件在连接的一端发生会出发连接两边都回调这个方法。
onMessageReceived()一个信息被从一端发送到另一端,当另一端收到的时候出发这个回调事件。
onPeerConnected() onPeerDisconnected()   当手机端和手表端连接和断开连接的时候调用。连接状态的变化会在连接的两端同时触发事件。
创建WearableListenerService
1:创建一个类继承WearableListenerService的服务
2:监听你感兴趣的事件,例如onDataChanged()

3:声明一个intent filter 在你的Android 清单文件中通知系统你的WearableListenerService。这个允许系统去bind绑定你的服务如果需要的话


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

        // Loop through the events and send a message
        // to the node that created the data item.
        for (DataEvent event : events) {
            Uri uri = event.getDataItem().getUri();

            // Get the node id from the host value of the URI
            String nodeId = uri.getHost();
            // Set the data of the message to be the bytes of the URI
            byte[] payload = uri.toString().getBytes();

            // Send the RPC
            Wearable.MessageApi.sendMessage(googleApiClient, nodeId,
                    DATA_ITEM_RECEIVED_PATH, payload);
        }
    }
}

Here's the corresponding intent filter in the Android manifest file:

<service android:name=".DataLayerListenerService">
  <intent-filter>
      <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
  </intent-filter>
</service>

数据层调用需要的权限:
为了传递数据层事件调用到你的应用,Google Play services 绑定了你的WearableListenerService。然后调用的回调通过IPC。这有一个结果是你的回调


继承了调用进程的权限。
如果你试着去操作一个特权的操作在回调里面,安全检查会失败,因为你的回调运行在调用进程的id而不是你的app的id。
为了修复这个,调用clearCallingIdentity(),等通过了IPC的边界的时候重新设置id,然后恢复id 在restoreCallingIdentity() 当你已经完成了特定的操作后。


long token = Binder.clearCallingIdentity();
try {
    performOperationRequiringPermissions();
} finally {
    Binder.restoreCallingIdentity(token);
}



第二种方法:使用有Listener 的acitivty

如果你的应用仅仅关心数据层事件,就是说用户正在和应用交互并且不需要一个长时间运行的服务去处理每个数据变化,你可以在activity 里面监听事件


通过实现下面的一个或者多个接口。
DataApi.DataListener
MessageApi.MessageListener
NodeApi.NodeListener
为了创建一个activity监听数据事件:
1:实现需要的接口
2:在onCreate(Bundle)的方法里面,创建一个GoogleApiClient的实例去访问Data Layer API数据层API
3:在onStart()方法里面,调用connect()方法去连接Google Play service 的客户端
4:当连接到Google Play services 的连接已经建立了,系统会调用onConnected()方法,这里你可以调用DataApi.addListener(),
MessageApi.addListener() 或者NodeApi.addListener() 去通知Google Play services 你的activity 专心于监听数据层事件。
5:在onStop()方法里面,取消注册监听事件,通过DataApi.removeListener(),MessageApi.removeListener() 或者NodeApi.removerListener()
6:依据你实现的接口实现onDataChanged(),onMessageReceived(),onPeerConnected() 和onPeerDisconnected()方法,

Here's an example that implements DataApi.DataListener:

public class MainActivity extends Activity implements
        DataApi.DataListener, ConnectionCallbacks, OnConnectionFailedListener {

    private GoogleApiClient mGoogleApiClient;

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

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

DataItemResult result = pendingResult.await();
if(result.getStatus().isSuccess()) {
    Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
}

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

        // Loop through the events and send a message
        // to the node that created the data item.
        for (DataEvent event : events) {
            Uri uri = event.getDataItem().getUri();

            // Get the node id from the host value of the URI
            String nodeId = uri.getHost();
            // Set the data of the message to be the bytes of the URI
            byte[] payload = uri.toString().getBytes();

            // Send the RPC
            Wearable.MessageApi.sendMessage(googleApiClient, nodeId,
                    DATA_ITEM_RECEIVED_PATH, payload);
        }
    }
}

Here's the corresponding intent filter in the Android manifest file:

<service android:name=".DataLayerListenerService">
  <intent-filter>
      <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
  </intent-filter>
</service>

long token = Binder.clearCallingIdentity();
try {
    performOperationRequiringPermissions();
} finally {
    Binder.restoreCallingIdentity(token);
}

猜你喜欢

转载自blog.csdn.net/Rodulf/article/details/50659837