| page.title=Handling Data Layer Events |
| |
| @jd:body |
| |
| <div id="tb-wrapper"> |
| <div id="tb"> |
| |
| <h2>This lesson teaches you to</h2> |
| <ol> |
| <li><a href="#Wait">Wait for the Status of Data Layer Calls</a></li> |
| <li><a href="#Listen">Listen for Data Layer Events</a></li> |
| </ol> |
| |
| </div> |
| </div> |
| |
| <p>When you make calls to the Data Layer API, you can receive the status |
| of the call when it completes as well as listen for any changes that |
| the call ends up making with listeners. |
| </p> |
| |
| <h2 id="Wait">Wait for the Status of Data Layer Calls</h2> |
| |
| <p>You'll notice that calls to the Data Layer API sometimes return a |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>, |
| such as |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>putDataItem()</code></a>. |
| As soon as the <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> is created, |
| the operation is queued in the background. If you do nothing else after this, the operation |
| eventually completes silently. However, you'll usually want to do something with the result |
| after the operation completes, so the |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> |
| lets you wait for the result status, either synchronously or asynchronously. |
| </p> |
| |
| <h3 id="async-waiting">Asynchronous calls</h3> |
| <p>If your code is running on the main UI thread, do not make blocking calls |
| to the Data Layer API. You can run the calls asynchronously by adding a callback method |
| to the <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> object, |
| which fires when the operation is completed:</p> |
| <pre> |
| 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()); |
| } |
| } |
| }); |
| </pre> |
| |
| <h3 id="sync-waiting">Synchronous calls</h3> |
| <p>If your code is running on a separate handler thread in a background service (which is the case |
| in a <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>), |
| it's fine for the calls to block. In this case, you can call |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html#await()"><code>await()</code></a> |
| on the <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> |
| object, which blocks until the request completes and returns a |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/Result.html"><code>Result</code></a> |
| object: |
| </p> |
| |
| <pre> |
| DataItemResult result = pendingResult.await(); |
| if(result.getStatus().isSuccess()) { |
| Log.d(TAG, "Data item set: " + result.getDataItem().getUri()); |
| } |
| </pre> |
| |
| |
| <h2 id="Listen">Listen for Data Layer Events</h2> |
| <p> |
| Because the data layer synchronizes and sends data across the handheld and |
| wearable, it is usually necessary to listen for important events. Examples of |
| such events include creation of data items and receipt of messages. |
| </p> |
| <p> |
| To listen for data layer events, you have two options: |
| </p> |
| <ul> |
| <li>Create a service that extends <a href |
| ="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html"> |
| {@code WearableListenerService}</a>.</li> |
| <li>Create an activity that implements <a |
| href="https://developer.android.com/reference/com/google/android/gms/wearable/DataApi.DataListener.html"> |
| {@code DataApi.DataListener}</a>.</li> |
| </ul> |
| <p> |
| With both these options, you override the data event callback methods for the |
| events you are interested in handling. |
| </p> |
| <h3>With a WearableListenerService</h3> |
| <p> |
| You typically create instances of this service in both your wearable and |
| handheld apps. If you are not interested in data events in one of these apps, |
| then you don't need to implement this service in that particular app. |
| </p> |
| <p> |
| For example, you can have a handheld app that sets and gets data item objects |
| and a wearable app that listens for these updates to update its UI. The |
| wearable never updates any of the data items, so the handheld app doesn't |
| listen for any data events from the wearable app. |
| </p> |
| <p> |
| Some of the events you can listen for using <a |
| href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"> |
| {@code WearableListenerService}</a> are as follows: |
| </p> |
| <ul> |
| <li><a |
| href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"> |
| {@code onDataChanged()}</a>: |
| Whenever a data item object is created, deleted, or changed, the system triggers |
| this callback on all connected nodes. |
| </li> |
| <li><a |
| href="http://developer.android.com/reference/com/google/android/gms/wearable/WearableListenerService.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)"> |
| {@code onMessageReceived()}</a>: A message sent from a node triggers |
| this callback on the target node.</li> |
| <li><a |
| href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html#onCapabilityChanged(com.google.android.gms.wearable.CapabilityInfo)"> |
| {@code onCapabilityChanged()}</a>: |
| When a capability that an instance of your app advertises becomes available |
| on the network, that event triggers this callback. If you're looking for a |
| nearby node you can query the |
| <a |
| href="https://developers.google.com/android/reference/com/google/android/gms/wearable/Node.html#isNearby()"> |
| {@code isNearby()}</a> method of the nodes provided in the callback.</li> |
| |
| <p> |
| In addition to those on this list, you can listen for events from |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener"> |
| {@code ChannelApi.ChannelListener}</a>, such as |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener.html#onChannelOpened(com.google.android.gms.wearable.Channel)"> |
| {@code onChannelOpened()}</a>. |
| </p> |
| </ul> |
| <p>To create a <a |
| href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>, follow these steps:</p> |
| |
| <ol> |
| <li>Create a class that extends |
| <a |
| href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>. |
| </li> |
| <li>Listen for the events that you're interested in, such as |
| <a |
| href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"> |
| <code>onDataChanged()</code></a>. |
| </li> |
| <li>Declare an intent filter in your Android manifest to notify the system |
| about your |
| <a |
| href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code> |
| WearableListenerService</code></a>. |
| This declaration allows the system to bind your service as needed. |
| </li> |
| </ol> |
| |
| <p>The following example shows how to implement a simple |
| <a |
| href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code> |
| WearableListenerService</code></a>: |
| </p> |
| <pre> |
| 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); |
| } |
| } |
| } |
| </pre> |
| |
| <p> |
| The next section explains how to use an intent filter with this listener. |
| </p> |
| |
| <h3>Using filters with WearableListenerService</h3> |
| |
| <p> |
| An intent filter for the |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html"> |
| {@code WearableListenerService}</a> example shown in the previous section might look like this: |
| |
| <pre> |
| <service android:name=".DataLayerListenerService"> |
| <intent-filter> |
| <action android:name="com.google.android.gms.wearable.DATA_CHANGED" /> |
| <data android:scheme="wear" android:host="*" |
| android:path="/start-activity" /> |
| </intent-filter> |
| </service> |
| </pre> |
| |
| <p> |
| In this filter, the {@code DATA_CHANGED} action replaces the |
| previously recommended {@code BIND_LISTENER} action so that only specific |
| events wake or launch your application. This change improves system efficiency |
| and reduces battery consumption and other overhead associated with your |
| application. In this example, the watch listens for the |
| {@code /start-activity} data item, and the |
| phone listens for the {@code /data-item-received} message response. |
| </p> |
| <p> |
| Standard Android filter matching rules apply. You can specify multiple services |
| per manifest, multiple intent filters per service, multiple actions per filter, |
| and multiple data stanzas per filter. Filters can match on a wildcard host or on |
| a specific one. To match on a wildcard host, use {@code host="*"}. To match |
| on a specific host, specify {@code host=<node_id>}. |
| </p> |
| |
| <p> |
| You can also match a literal path or path prefix. If you are matching by path |
| or path prefix, you must specify a wildcard or specific host. |
| If you do not do so, the system ignores the path you specified. |
| </p> |
| |
| <p> |
| For more information on the filter types that Wear supports, see the |
| API reference documentation for <a |
| href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService"> |
| {@code WearableListenerService}</a>. |
| </p> |
| |
| <p> |
| For more information on data filters and matching rules, see the API reference |
| documentation for the <a |
| href="{@docRoot}guide/topics/manifest/data-element.html">{@code data}</a> |
| manifest element. |
| </p> |
| |
| <p>When matching intent filters, there are two important rules to remember:</p> |
| <ul> |
| <li>If a scheme is not specified for the intent filter, the system ignores |
| all the other URI attributes.</li> |
| <li>If no host is specified for the filter, the system ignores the |
| port attribute and all the path attributes.</li> |
| </ul> |
| |
| <h3>With a listener activity</h3> |
| <p> |
| If your app only cares about data-layer events when the user is interacting |
| with the app, it may not need a long-running service to handle every data |
| change. In such a case, you can listen for events in an activity by |
| implementing one or more of the following interfaces: |
| </p> |
| <ul> |
| <li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code> |
| DataApi.DataListener</code></a></li> |
| |
| <li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.MessageListener.html"> |
| <code>MessageApi.MessageListener</code></a></li> |
| |
| <li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/CapabilityApi.CapabilityListener.html">{@code CapabilityApi.CapabilityListener}</a></li> |
| </ul> |
| |
| <p>To create an activity that listens for data events:</p> |
| <ol> |
| <li>Implement the desired interfaces.</li> |
| <li>In {@link android.app.Activity#onCreate onCreate()}, create an instance of |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code> |
| </a>to work with the Data Layer API.</li> |
| |
| <li> |
| In {@link android.app.Activity#onStart onStart()}, call <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"> |
| <code>connect()</code></a> to connect the client to Google Play services. |
| </li> |
| |
| <li>When the connection to Google Play services is established, the system calls |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"><code>onConnected()</code></a>. This is where you call |
| |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.DataApi.DataListener)"> |
| <code>DataApi.addListener()</code></a>, |
| |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener, android.net.Uri, int)"> |
| <code>MessageApi.addListener()</code></a>, or |
| |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/CapabilityApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient,%20com.google.android.gms.wearable.CapabilityApi.CapabilityListener,%20android.net.Uri,%20int)"> |
| {@code CapabilityApi.addListener()}</a> to notify Google Play services that your activity is |
| interested in listening for data layer events.</li> |
| |
| <li>In {@link android.app.Activity#onStop onStop()}, unregister any listeners with |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.DataApi.DataListener)"><code>DataApi.removeListener()</code></a>, |
| |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener)"><code>MessageApi.removeListener()</code></a>, or |
| |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/CapabilityApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.CapabilityApi.CapabilityListener)"> |
| {@code CapabilityApi.removeListener()}</a>.</li> |
| |
| |
| <p>An alternative to adding listeners in |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"><code>onConnected()</code></a> |
| and removing them in |
| {@link android.app.Activity#onStop onStop()} is to add a filtered listener in an activity’s {@link android.app.Activity#onResume onResume()} and |
| remove it in {@link android.app.Activity#onPause onPause()}, so as to only receive data that is relevant to the |
| current application state.</p> |
| |
| |
| <li>Implement |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"> |
| <code>onDataChanged()</code></a>, |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.MessageListener.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)"> |
| <code>onMessageReceived()</code></a>, |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html#onCapabilityChanged(com.google.android.gms.wearable.CapabilityInfo)"> |
| {@code onCapabilityChanged()}</a>, |
| or methods from <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener.html"> |
| Channel API listener methods</a>, depending on the interfaces that you implemented.</li> |
| </ol> |
| |
| <p>Here's an example that implements |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>:</p> |
| |
| <pre> |
| 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()); |
| } |
| } |
| } |
| } |
| </pre> |
| <h3>Using Filters with Listener Activities</h3> |
| <p> |
| Just as you can specify intent filters for manifest-based |
| <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html"> |
| <code>WearableListenerService</code></a> objects, you can also use intent filters when registering a |
| listener through the Wearable API. The same rules are applicable to both |
| API-based listeners manifest-based listeners. |
| </p> |
| |
| <p> |
| A common pattern is to register a listener with a specific path or path prefix |
| in an activity’s {@link android.app.Activity#onResume onResume()} method, and to |
| remove the listener in the activity’s {@link android.app.Activity#onPause onPause()} method. |
| Implementing listeners in this fashion allows your application to more selectively receive events, |
| improving its design and efficiency. |
| </p> |