Home | History | Annotate | Download | only in data-layer
      1 page.title=Handling Data Layer Events
      2 
      3 @jd:body
      4 
      5 <div id="tb-wrapper">
      6 <div id="tb">
      7 
      8 <h2>This lesson teaches you to</h2>
      9 <ol>
     10   <li><a href="#Wait">Wait for the Status of Data Layer Calls</a></li>
     11   <li><a href="#Listen">Listen for Data Layer Events</a></li>
     12 </ol>
     13 
     14 </div>
     15 </div>
     16 
     17 <p>When you make calls to the Data Layer API, you can receive the status
     18 of the call when it completes as well as listen for any changes that
     19 the call ends up making with listeners.
     20 </p>
     21 
     22 <h2 id="Wait">Wait for the Status of Data Layer Calls</h2>
     23 
     24 <p>You'll notice that calls to the Data Layer API sometimes return a
     25 <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>,
     26 such as
     27 <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>.
     28 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,
     29 the operation is queued in the background. If you do nothing else after this, the operation
     30 eventually completes silently. However, you'll usually want to do something with the result
     31 after the operation completes, so the
     32 <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>
     33 lets you wait for the result status, either synchronously or asynchronously.
     34 </p>
     35 
     36 <h3 id="async-waiting">Asynchronous calls</h3>
     37 <p>If your code is running on the main UI thread, do not make blocking calls
     38 to the Data Layer API. You can run the calls asynchronously by adding a callback method
     39 to the <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> object,
     40 which fires when the operation is completed:</p>
     41 <pre>
     42 pendingResult.setResultCallback(new ResultCallback&lt;DataItemResult&gt;() {
     43     &#64;Override
     44     public void onResult(final DataItemResult result) {
     45         if(result.getStatus().isSuccess()) {
     46             Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
     47         }
     48     }
     49 });
     50 </pre>
     51 
     52 <h3 id="sync-waiting">Synchronous calls</h3>
     53 <p>If your code is running on a separate handler thread in a background service (which is the case
     54 in a <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>),
     55 it's fine for the calls to block. In this case, you can call
     56 <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html#await()"><code>await()</code></a>
     57 on the <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>
     58 object, which blocks until the request completes and returns a
     59 <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/Result.html"><code>Result</code></a>
     60 object:
     61 </p>
     62 
     63 <pre>
     64 DataItemResult result = pendingResult.await();
     65 if(result.getStatus().isSuccess()) {
     66     Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
     67 }
     68 </pre>
     69 
     70 
     71 <h2 id="Listen">Listen for Data Layer Events</h2>
     72 <p>
     73 Because the data layer synchronizes and sends data across the handheld and
     74 wearable, it is usually necessary to listen for important events. Examples of
     75 such events include creation of data items and receipt of messages.
     76 </p>
     77 <p>
     78 To listen for data layer events, you have two options:
     79 </p>
     80 <ul>
     81    <li>Create a service that extends <a href
     82 ="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html">
     83 {@code WearableListenerService}</a>.</li>
     84    <li>Create an activity that implements <a
     85 href="https://developer.android.com/reference/com/google/android/gms/wearable/DataApi.DataListener.html">
     86 {@code DataApi.DataListener}</a>.</li>
     87 </ul>
     88 <p>
     89 With both these options, you override the data event callback methods for the
     90 events you are interested in handling.
     91 </p>
     92 <h3>With a WearableListenerService</h3>
     93 <p>
     94 You typically create instances of this service in both your wearable and
     95 handheld apps. If you are not interested in data events in one of these apps,
     96 then you don't need to implement this service in that particular app.
     97 </p>
     98 <p>
     99 For example, you can have a handheld app that sets and gets data item objects
    100 and a wearable app that listens for these updates to update its UI. The
    101 wearable never updates any of the data items, so the handheld app doesn't
    102 listen for any data events from the wearable app.
    103 </p>
    104 <p>
    105 Some of the events you can listen for using <a
    106 href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html">
    107 {@code WearableListenerService}</a> are as follows:
    108 </p>
    109 <ul>
    110   <li><a
    111 href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)">
    112 {@code onDataChanged()}</a>:
    113 Whenever a data item object is created, deleted, or changed, the system triggers
    114 this callback on all connected nodes.
    115 </li>
    116 <li><a
    117 href="http://developer.android.com/reference/com/google/android/gms/wearable/WearableListenerService.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)">
    118 {@code onMessageReceived()}</a>:  A message sent from a node triggers
    119 this callback on the target node.</li>
    120 <li><a
    121 href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html#onCapabilityChanged(com.google.android.gms.wearable.CapabilityInfo)">
    122 {@code onCapabilityChanged()}</a>:
    123 When a capability that an instance of your app advertises becomes available
    124 on the network, that event triggers this callback. If you're looking for a
    125 nearby node you can query the
    126 <a
    127 href="https://developers.google.com/android/reference/com/google/android/gms/wearable/Node.html#isNearby()">
    128 {@code isNearby()}</a> method of the nodes provided in the callback.</li>
    129 
    130 <p>
    131 In addition to those on this list, you can listen for events from
    132 <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener">
    133 {@code ChannelApi.ChannelListener}</a>, such as
    134 <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener.html#onChannelOpened(com.google.android.gms.wearable.Channel)">
    135 {@code onChannelOpened()}</a>.
    136 </p>
    137 </ul>
    138 <p>To create a <a
    139 href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>, follow these steps:</p>
    140 
    141 <ol>
    142   <li>Create a class that extends
    143   <a
    144 href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
    145   </li>
    146   <li>Listen for the events that you're interested in, such as
    147   <a
    148 href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)">
    149 <code>onDataChanged()</code></a>.
    150   </li>
    151   <li>Declare an intent filter in your Android manifest to notify the system
    152 about your
    153   <a
    154 href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>
    155 WearableListenerService</code></a>.
    156   This declaration allows the system to bind your service as needed.
    157   </li>
    158 </ol>
    159 
    160   <p>The following example shows how to implement a simple
    161   <a
    162 href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>
    163 WearableListenerService</code></a>:
    164   </p>
    165 <pre>
    166 public class DataLayerListenerService extends WearableListenerService {
    167 
    168     private static final String TAG = "DataLayerSample";
    169     private static final String START_ACTIVITY_PATH = "/start-activity";
    170     private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";
    171 
    172     &#64;Override
    173     public void onDataChanged(DataEventBuffer dataEvents) {
    174         if (Log.isLoggable(TAG, Log.DEBUG)) {
    175             Log.d(TAG, "onDataChanged: " + dataEvents);
    176         }
    177         final List events = FreezableUtils
    178                 .freezeIterable(dataEvents);
    179 
    180         GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
    181                 .addApi(Wearable.API)
    182                 .build();
    183 
    184         ConnectionResult connectionResult =
    185                 googleApiClient.blockingConnect(30, TimeUnit.SECONDS);
    186 
    187         if (!connectionResult.isSuccess()) {
    188             Log.e(TAG, "Failed to connect to GoogleApiClient.");
    189             return;
    190         }
    191 
    192         // Loop through the events and send a message
    193         // to the node that created the data item.
    194         for (DataEvent event : events) {
    195             Uri uri = event.getDataItem().getUri();
    196 
    197             // Get the node id from the host value of the URI
    198             String nodeId = uri.getHost();
    199             // Set the data of the message to be the bytes of the URI
    200             byte[] payload = uri.toString().getBytes();
    201 
    202             // Send the RPC
    203             Wearable.MessageApi.sendMessage(googleApiClient, nodeId,
    204                     DATA_ITEM_RECEIVED_PATH, payload);
    205         }
    206     }
    207 }
    208 </pre>
    209 
    210 <p>
    211 The next section explains how to use an intent filter with this listener.
    212 </p>
    213 
    214 <h3>Using filters with WearableListenerService</h3>
    215 
    216 <p>
    217 An intent filter for the
    218 <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html">
    219 {@code WearableListenerService}</a> example shown in the previous section might look like this:
    220 
    221 <pre>
    222 &lt;service android:name=".DataLayerListenerService"&gt;
    223   &lt;intent-filter>
    224       &lt;action android:name="com.google.android.gms.wearable.DATA_CHANGED" /&gt;
    225       &lt;data android:scheme="wear" android:host="*"
    226                android:path="/start-activity" /&gt;
    227   &lt;/intent-filter&gt;
    228 &lt;/service&gt;
    229 </pre>
    230 
    231 <p>
    232 In this filter, the {@code DATA_CHANGED} action replaces the
    233 previously recommended {@code BIND_LISTENER} action so that only specific
    234 events wake or launch your application. This change improves system efficiency
    235 and reduces battery consumption and other overhead associated with your
    236 application. In this example, the watch listens for the
    237 {@code /start-activity} data item, and the
    238 phone listens for the {@code /data-item-received} message response.
    239 </p>
    240 <p>
    241 Standard Android filter matching rules apply. You can specify multiple services
    242 per manifest, multiple intent filters per service, multiple actions per filter,
    243 and multiple data stanzas per filter. Filters can match on a wildcard host or on
    244 a specific one. To match on a wildcard host, use {@code host="*"}. To match
    245 on a specific host, specify {@code host=<node_id>}.
    246 </p>
    247 
    248 <p>
    249 You can also match a literal path or path prefix. If you are matching by path
    250 or path prefix, you must specify a wildcard or specific host.
    251 If you do not do so, the system ignores the path you specified.
    252 </p>
    253 
    254 <p>
    255 For more information on the filter types that Wear supports, see the
    256 API reference documentation for <a
    257 href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService">
    258 {@code WearableListenerService}</a>.
    259 </p>
    260 
    261 <p>
    262 For more information on data filters and matching rules, see the API reference
    263 documentation for the <a
    264 href="{@docRoot}guide/topics/manifest/data-element.html">{@code data}</a>
    265 manifest element.
    266 </p>
    267 
    268 <p>When matching intent filters, there are two important rules to remember:</p>
    269 <ul>
    270     <li>If a scheme is not specified for the intent filter, the system ignores
    271     all the other URI attributes.</li>
    272     <li>If no host is specified for the filter, the system ignores the
    273     port attribute and all the path attributes.</li>
    274 </ul>
    275 
    276 <h3>With a listener activity</h3>
    277 <p>
    278 If your app only cares about data-layer events when the user is interacting
    279 with the app, it may not need a long-running service to handle every data
    280 change. In such a case, you can listen for events in an activity by
    281 implementing one or more of the following interfaces:
    282 </p>
    283 <ul>
    284   <li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>
    285   DataApi.DataListener</code></a></li>
    286 
    287   <li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.MessageListener.html">
    288   <code>MessageApi.MessageListener</code></a></li>
    289 
    290   <li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/CapabilityApi.CapabilityListener.html">{@code CapabilityApi.CapabilityListener}</a></li>
    291 </ul>
    292 
    293 <p>To create an activity that listens for data events:</p>
    294 <ol>
    295 <li>Implement the desired interfaces.</li>
    296 <li>In {@link android.app.Activity#onCreate onCreate()}, create an instance of
    297 <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code>
    298 </a>to work with the Data Layer API.</li>
    299 
    300 <li>
    301 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()">
    302 <code>connect()</code></a> to connect the client to Google Play services.
    303 </li>
    304 
    305 <li>When the connection to Google Play services is established, the system calls
    306 <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
    307 
    308 <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)">
    309 <code>DataApi.addListener()</code></a>,
    310 
    311 <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)">
    312 <code>MessageApi.addListener()</code></a>, or
    313 
    314 <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)">
    315 {@code CapabilityApi.addListener()}</a> to notify Google Play services that your activity is
    316 interested in listening for data layer events.</li>
    317 
    318 <li>In {@link android.app.Activity#onStop onStop()}, unregister any listeners with
    319 <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>,
    320 
    321 <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
    322 
    323 <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)">
    324 {@code CapabilityApi.removeListener()}</a>.</li>
    325 
    326 
    327 <p>An alternative to adding listeners in
    328 <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>
    329 and removing them in
    330 {@link android.app.Activity#onStop onStop()} is to add a filtered listener in an activitys {@link android.app.Activity#onResume onResume()} and
    331 remove it in {@link android.app.Activity#onPause onPause()}, so as to only receive data that is relevant to the
    332 current application state.</p>
    333 
    334 
    335 <li>Implement
    336 <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)">
    337 <code>onDataChanged()</code></a>,
    338   <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.MessageListener.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)">
    339   <code>onMessageReceived()</code></a>,
    340   <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html#onCapabilityChanged(com.google.android.gms.wearable.CapabilityInfo)">
    341 {@code onCapabilityChanged()}</a>,
    342 or methods from <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener.html">
    343 Channel API listener methods</a>, depending on the interfaces that you implemented.</li>
    344 </ol>
    345 
    346 <p>Here's an example that implements
    347 <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>:</p>
    348 
    349 <pre>
    350 public class MainActivity extends Activity implements
    351         DataApi.DataListener, ConnectionCallbacks, OnConnectionFailedListener {
    352 
    353     private GoogleApiClient mGoogleApiClient;
    354 
    355     &#64;Override
    356     protected void onCreate(Bundle savedInstanceState) {
    357         super.onCreate(savedInstanceState);
    358 
    359         setContentView(R.layout.main);
    360         mGoogleApiClient = new GoogleApiClient.Builder(this)
    361                 .addApi(Wearable.API)
    362                 .addConnectionCallbacks(this)
    363                 .addOnConnectionFailedListener(this)
    364                 .build();
    365     }
    366 
    367     &#64;Override
    368     protected void onStart() {
    369         super.onStart();
    370         if (!mResolvingError) {
    371             mGoogleApiClient.connect();
    372         }
    373     }
    374 
    375     &#64;Override
    376     public void onConnected(Bundle connectionHint) {
    377         if (Log.isLoggable(TAG, Log.DEBUG)) {
    378             Log.d(TAG, "Connected to Google Api Service");
    379         }
    380         Wearable.DataApi.addListener(mGoogleApiClient, this);
    381     }
    382 
    383     &#64;Override
    384     protected void onStop() {
    385         if (null != mGoogleApiClient && mGoogleApiClient.isConnected()) {
    386             Wearable.DataApi.removeListener(mGoogleApiClient, this);
    387             mGoogleApiClient.disconnect();
    388         }
    389         super.onStop();
    390     }
    391 
    392     &#64;Override
    393     public void onDataChanged(DataEventBuffer dataEvents) {
    394         for (DataEvent event : dataEvents) {
    395             if (event.getType() == DataEvent.TYPE_DELETED) {
    396                 Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
    397             } else if (event.getType() == DataEvent.TYPE_CHANGED) {
    398                 Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
    399             }
    400         }
    401     }
    402 }
    403 </pre>
    404 <h3>Using Filters with Listener Activities</h3>
    405 <p>
    406 Just as you can specify intent filters for manifest-based
    407 <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html">
    408 <code>WearableListenerService</code></a> objects, you can also use intent filters when registering a
    409 listener through the Wearable API. The same rules are applicable to both
    410 API-based listeners manifest-based listeners.
    411 </p>
    412 
    413 <p>
    414 A common pattern is to register a listener with a specific path or path prefix
    415 in an activitys {@link android.app.Activity#onResume onResume()} method, and to
    416 remove the listener in the activitys {@link android.app.Activity#onPause onPause()} method.
    417 Implementing listeners in this fashion allows your application to more selectively receive events,
    418 improving its design and efficiency.
    419 </p>
    420