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 with the data layer, 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="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>,
     26 such as
     27 <a href="{@docRoot}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="{@docRoot}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="{@docRoot}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">Asynchronously waiting</h3>
     37 <p>If your code is running on the main UI thread, do not making blocking calls
     38 to the data layer API. You can run the calls asynchronously by adding a callback
     39 to the <a href="{@docRoot}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">Synchronously waiting</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="{@docRoot}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="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html#await()"><code>await()</code></a>
     57 on the PendingResult object, which will block until the request has completed, and return a Result
     58 object:
     59 </p>
     60 
     61 <pre>
     62 DataItemResult result = pendingResult.await();
     63 if(result.getStatus().isSuccess()) {
     64     Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
     65 }
     66 </pre>
     67 
     68 
     69 <h2 id="Listen">Listen for Data Layer Events </h2>
     70 <p>Because the data layer synchronizes and sends data across the handheld and
     71 wearable, you normally want to listen for important events, such as when data items
     72 are created, messages are received, or when the wearable and handset are connected.
     73 </p>
     74 <p>To listen for data layer events, you have two options:</p>
     75 
     76 <ul>
     77   <li>Create a service that extends
     78   <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
     79   </li>
     80   <li>Create an activity that implements
     81   <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>.
     82   </li>
     83 </ul>
     84 
     85 <p>With both these options, you override any of the data event callbacks that you care about
     86 handling in your implementation.</p>
     87 
     88 <h3 id="listener-service">With a WearableListenerService</h3>
     89 
     90 <p>
     91 You typically create instances of this service in both your wearable and handheld apps. If you
     92 don't care about data events in one of these apps, then you don't need to implement this
     93 service in that particular app.</p>
     94 
     95 <p>For example, you can have a handheld app that sets and gets data item objects and a wearable app
     96 that listens for these updates to update it's UI. The wearable never updates any of the data items,
     97 so the handheld app doesn't listen for any data events from the wearable app.</p>
     98 
     99 <p>You can listen for the following events with
    100 <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>:</p>
    101 
    102 <ul>
    103   <li><a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code></a>
    104 - Called when data item objects are created, changed, or deleted. An event on one side of a connection
    105 triggers this callback on both sides.</li>
    106   <li><a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)"><code>onMessageReceived()</code></a>
    107 -  A message sent from one side of a connection triggers this callback on the other side of the connection.</li>
    108   <li><a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)"><code>onPeerConnected()</code></a>
    109   and <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onPeerDisconnected(com.google.android.gms.wearable.Node)"><code>onPeerDisconnected()</code></a> -
    110   Called when connection with the handheld or wearable is connected or disconnected.
    111   Changes in connection state on one side of the connection triggers these callbacks on both sides of the connection.
    112   </li>
    113 </ul>
    114 
    115 <p>To create a <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>:</p>
    116 
    117 <ol>
    118   <li>Create a class that extends
    119   <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
    120   </li>
    121   <li>Listen for the events that you care about, such as
    122   <a href="{@docRoot}/reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code></a>.
    123   </li>
    124   <li>Declare an intent filter in your Android manifest to notify the system about your
    125   <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
    126   This allows the system to bind your service as needed.
    127   </li>
    128 </ol>
    129 
    130   <p>The following example shows how to implement a simple
    131   <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>:
    132   </p>
    133 
    134 <pre>
    135 public class DataLayerListenerService extends WearableListenerService {
    136 
    137     private static final String TAG = "DataLayerSample";
    138     private static final String START_ACTIVITY_PATH = "/start-activity";
    139     private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";
    140 
    141     &#64;Override
    142     public void onDataChanged(DataEventBuffer dataEvents) {
    143         if (Log.isLoggable(TAG, Log.DEBUG)) {
    144             Log.d(TAG, "onDataChanged: " + dataEvents);
    145         }
    146         final List<DataEvent> events = FreezableUtils
    147                 .freezeIterable(dataEvents);
    148 
    149         GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
    150                 .addApi(Wearable.API)
    151                 .build();
    152 
    153         ConnectionResult connectionResult =
    154                 googleApiClient.blockingConnect(30, TimeUnit.SECONDS);
    155 
    156         if (!connectionResult.isSuccess()) {
    157             Log.e(TAG, "Failed to connect to GoogleApiClient.");
    158             return;
    159         }
    160 
    161         // Loop through the events and send a message
    162         / to the node that created the data item.
    163         for (DataEvent event : events) {
    164             Uri uri = event.getDataItem().getUri();
    165 
    166             // Get the node id from the host value of the URI
    167             String nodeId = uri.getHost();
    168             // Set the data of the message to be the bytes of the URI.
    169             byte[] payload = uri.toString().getBytes();
    170 
    171             // Send the RPC
    172             Wearable.MessageApi.sendMessage(googleApiClient, nodeId,
    173                     DATA_ITEM_RECEIVED_PATH, payload);
    174         }
    175     }
    176 }
    177 </pre>
    178 
    179 <p>Here's the corresponding intent filter in the Android manifest file:</p>
    180 
    181 <pre>
    182 &lt;service android:name=".DataLayerListenerService"&gt;
    183   &lt;intent-filter&gt;
    184       &lt;action android:name="com.google.android.gms.wearable.BIND_LISTENER" /&gt;
    185   &lt;/intent-filter&gt;
    186 &lt;/service&gt;
    187 </pre>
    188 
    189 
    190 <h4>Permissions within Data Layer Callbacks</h4>
    191 
    192 <p>In order to deliver callbacks to your application for data layer events, Google Play services
    193 binds to your <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>,
    194 and calls your callbacks via IPC. This has the consequence
    195 that your callbacks inherit the permissions of the calling process.</p>
    196 
    197 <p>If you try to perform a privileged operation within a callback, the security check fails because your callback is
    198 running with the identity of the calling process, instead of the identity of your app's
    199 process.</p>
    200 
    201 <p>To fix this, call {@link android.os.Binder#clearCallingIdentity} </a>,
    202 to reset identity after crossing the IPC boundary, and then restore identity with
    203 {@link android.os.Binder#restoreCallingIdentity restoreCallingIdentity()} when
    204 you've completed the privileged operation:
    205 </p>
    206 
    207 <pre>
    208 long token = Binder.clearCallingIdentity();
    209 try {
    210     performOperationRequiringPermissions();
    211 } finally {
    212     Binder.restoreCallingIdentity(token);
    213 }
    214 </pre>
    215 
    216 <h3 id="Listen">With a Listener Activity</h3>
    217 
    218 <p>
    219 If your app only cares about data layer events when the user is interacting
    220 with the app and does not need a long-running service to handle every data
    221 change, you can listen for events in an activity by implementing one or more
    222 of the following interfaces:
    223 
    224 <ul>
    225   <li><a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a></li>
    226   <li><a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.MessageListener.html"><code>MessageApi.MessageListener</code></a></li>
    227   <li><a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html"><code>NodeApi.NodeListener</code></a></li>
    228 </ul>
    229 </p>
    230 
    231 <p>To create an activity that listens for data events:</p>
    232 <ol>
    233 <li>Implement the desired interfaces.</li>
    234 <li>In {@link android.app.Activity#onCreate}, create an instance of
    235 <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code></a>
    236 to work with the data layer API.
    237 <li>
    238 In {@link android.app.Activity#onStart onStart()}, call <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"><code>connect()</code></a> to connect the client to Google Play services.
    239 </li>
    240 <li>When the connection to Google Play services is established, the system calls
    241 <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"><code>onConnected()</code></a>. This is where you call
    242 <a href="{@docRoot}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>,
    243   <a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener)"><code>MessageApi.addListener()</code></a>,
    244   or <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.NodeApi.NodeListener)"><code>NodeApi.addListener()</code></a>
    245   to notify Google Play services that your activity is interested in listening for data layer events.
    246 </li>
    247 <li>In {@link android.app.Activity#onStop onStop()}, unregister any listeners with
    248 <a href="{@docRoot}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>,
    249 <a href="{@docRoot}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>,
    250 or <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.NodeApi.NodeListener)"><code>NodeApi.removeListener()</code></a>.
    251 </li>
    252 <li>Implement <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code>,
    253   <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html#onPeerConnected(com.google.android.gms.wearable.Node)"><code>onMessageReceived()</code></a>,
    254     <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html#onPeerConnected(com.google.android.gms.wearable.Node)"><code>onPeerConnected()</code></a>, and
    255   <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html#onPeerDisconnected(com.google.android.gms.wearable.Node)"><code>onPeerDisconnected()</code></a>, depending on the interfaces that you implemented.
    256 </li>
    257 </ol>
    258 
    259 <p>Here's an example that implements
    260 <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>:</p>
    261 
    262 <pre>
    263 public class MainActivity extends Activity implements
    264         DataApi.DataListener, ConnectionCallbacks, OnConnectionFailedListener {
    265 
    266     &#64;Override
    267     protected void onCreate(Bundle savedInstanceState) {
    268         super.onCreate(savedInstanceState);
    269 
    270         setContentView(R.layout.main);
    271         mGoogleApiClient = new GoogleApiClient.Builder(this)
    272                 .addApi(Wearable.API)
    273                 .addConnectionCallbacks(this)
    274                 .addOnConnectionFailedListener(this)
    275                 .build();
    276     }
    277 
    278     &#64;Override
    279     protected void onStart() {
    280         super.onStart();
    281         if (!mResolvingError) {
    282             mGoogleApiClient.connect();
    283         }
    284     }
    285 
    286    &#64;Override
    287     public void onConnected(Bundle connectionHint) {
    288         if (Log.isLoggable(TAG, Log.DEBUG)) {
    289             Log.d(TAG, "Connected to Google Api Service");
    290         }
    291         Wearable.DataApi.addListener(mGoogleApiClient, this);
    292     }
    293 
    294     &#64;Override
    295     protected void onStop() {
    296         if (null != mGoogleApiClient && mGoogleApiClient.isConnected()) {
    297             Wearable.DataApi.removeListener(mGoogleApiClient, this);
    298             mGoogleApiClient.disconnect();
    299         }
    300         super.onStop();
    301     }
    302 
    303     &#64;Override
    304     public void onDataChanged(DataEventBuffer dataEvents) {
    305         for (DataEvent event : dataEvents) {
    306             if (event.getType() == DataEvent.TYPE_DELETED) {
    307                 Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
    308             } else if (event.getType() == DataEvent.TYPE_CHANGED) {
    309                  Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
    310             }
    311         }
    312     }
    313 </pre>