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<DataItemResult>() { 43 @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 @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 <service android:name=".DataLayerListenerService"> 186 <intent-filter> 187 <action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> 188 </intent-filter> 189 </service> 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 @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 @Override 285 protected void onStart() { 286 super.onStart(); 287 if (!mResolvingError) { 288 mGoogleApiClient.connect(); 289 } 290 } 291 292 @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 @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 @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