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<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">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 @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 <service android:name=".DataLayerListenerService"> 183 <intent-filter> 184 <action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> 185 </intent-filter> 186 </service> 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 @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 @Override 279 protected void onStart() { 280 super.onStart(); 281 if (!mResolvingError) { 282 mGoogleApiClient.connect(); 283 } 284 } 285 286 @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 @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 @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>