首先要注意的是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的时候
-------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------
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()。
-------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------
一般不直接使用DataItem ,而是使用PutDataRequest 对象,
1:创建一个PutDataRequest对象,分配一个唯一标识字符路劲path给数据item。
2:使用setData 来设置payload,
3:使用DataApi.putDataItem()来请求系统创建数据项DataItem。
4: 当请求数据项的时候,系统会将实现了DataItem 接口的对象返回。
然后,对于setData()来说,我们一般使用data map 来替代它,这个data map在一个可以轻松使用的Bundle类似的接口里面 将数据项释放出来。
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) { ... } ... }
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
传送资产,就是传送大的二进制数据
为了通过蓝牙发送大的blob类型的二进制数据,例如图片,将Asset添加到数据项data item 上面,将数据项放到重复的数据存储。
assets 资产会自动的处理缓存数据来防止重复的传送和保护蓝牙带宽。
一个常用的模式是手机端下载了一个图片,将这个图片缩放到一个合适尺度可以放到手表端。
同时将这个传送到手表端作为一个资产asset。下面的例子演示了这个模式
注意:虽然data item 的大小限制在100kb,但是资产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()); }
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>
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方法在下面会解释。
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.有细密纹理的,细粒的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; }
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; }
@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); } }
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()); } } });同步调用:
这样的话你可以调用PendingResult对象的await()的方法,当请求完成并且返回一个Result对象的时候阻塞。
DataItemResult result = pendingResult.await(); if(result.getStatus().isSuccess()) { Log.d(TAG, "Data item set: " + result.getDataItem().getUri()); }
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>
long token = Binder.clearCallingIdentity(); try { performOperationRequiringPermissions(); } finally { Binder.restoreCallingIdentity(token); }
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); }