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