420 lines
19 KiB
Plaintext
420 lines
19 KiB
Plaintext
page.title=Handling Data Layer Events
|
||
|
||
@jd:body
|
||
|
||
<div id="tb-wrapper">
|
||
<div id="tb">
|
||
|
||
<h2>This lesson teaches you to</h2>
|
||
<ol>
|
||
<li><a href="#Wait">Wait for the Status of Data Layer Calls</a></li>
|
||
<li><a href="#Listen">Listen for Data Layer Events</a></li>
|
||
</ol>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<p>When you make calls to the Data Layer API, you can receive the status
|
||
of the call when it completes as well as listen for any changes that
|
||
the call ends up making with listeners.
|
||
</p>
|
||
|
||
<h2 id="Wait">Wait for the Status of Data Layer Calls</h2>
|
||
|
||
<p>You'll notice that calls to the Data Layer API sometimes return a
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>,
|
||
such as
|
||
<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>.
|
||
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,
|
||
the operation is queued in the background. If you do nothing else after this, the operation
|
||
eventually completes silently. However, you'll usually want to do something with the result
|
||
after the operation completes, so the
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>
|
||
lets you wait for the result status, either synchronously or asynchronously.
|
||
</p>
|
||
|
||
<h3 id="async-waiting">Asynchronous calls</h3>
|
||
<p>If your code is running on the main UI thread, do not make blocking calls
|
||
to the Data Layer API. You can run the calls asynchronously by adding a callback method
|
||
to the <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> object,
|
||
which fires when the operation is completed:</p>
|
||
<pre>
|
||
pendingResult.setResultCallback(new ResultCallback<DataItemResult>() {
|
||
@Override
|
||
public void onResult(final DataItemResult result) {
|
||
if(result.getStatus().isSuccess()) {
|
||
Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
|
||
}
|
||
}
|
||
});
|
||
</pre>
|
||
|
||
<h3 id="sync-waiting">Synchronous calls</h3>
|
||
<p>If your code is running on a separate handler thread in a background service (which is the case
|
||
in a <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>),
|
||
it's fine for the calls to block. In this case, you can call
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html#await()"><code>await()</code></a>
|
||
on the <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>
|
||
object, which blocks until the request completes and returns a
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/Result.html"><code>Result</code></a>
|
||
object:
|
||
</p>
|
||
|
||
<pre>
|
||
DataItemResult result = pendingResult.await();
|
||
if(result.getStatus().isSuccess()) {
|
||
Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
|
||
}
|
||
</pre>
|
||
|
||
|
||
<h2 id="Listen">Listen for Data Layer Events</h2>
|
||
<p>
|
||
Because the data layer synchronizes and sends data across the handheld and
|
||
wearable, it is usually necessary to listen for important events. Examples of
|
||
such events include creation of data items and receipt of messages.
|
||
</p>
|
||
<p>
|
||
To listen for data layer events, you have two options:
|
||
</p>
|
||
<ul>
|
||
<li>Create a service that extends <a href
|
||
="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html">
|
||
{@code WearableListenerService}</a>.</li>
|
||
<li>Create an activity that implements <a
|
||
href="https://developer.android.com/reference/com/google/android/gms/wearable/DataApi.DataListener.html">
|
||
{@code DataApi.DataListener}</a>.</li>
|
||
</ul>
|
||
<p>
|
||
With both these options, you override the data event callback methods for the
|
||
events you are interested in handling.
|
||
</p>
|
||
<h3>With a WearableListenerService</h3>
|
||
<p>
|
||
You typically create instances of this service in both your wearable and
|
||
handheld apps. If you are not interested in data events in one of these apps,
|
||
then you don't need to implement this service in that particular app.
|
||
</p>
|
||
<p>
|
||
For example, you can have a handheld app that sets and gets data item objects
|
||
and a wearable app that listens for these updates to update its UI. The
|
||
wearable never updates any of the data items, so the handheld app doesn't
|
||
listen for any data events from the wearable app.
|
||
</p>
|
||
<p>
|
||
Some of the events you can listen for using <a
|
||
href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html">
|
||
{@code WearableListenerService}</a> are as follows:
|
||
</p>
|
||
<ul>
|
||
<li><a
|
||
href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)">
|
||
{@code onDataChanged()}</a>:
|
||
Whenever a data item object is created, deleted, or changed, the system triggers
|
||
this callback on all connected nodes.
|
||
</li>
|
||
<li><a
|
||
href="http://developer.android.com/reference/com/google/android/gms/wearable/WearableListenerService.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)">
|
||
{@code onMessageReceived()}</a>: A message sent from a node triggers
|
||
this callback on the target node.</li>
|
||
<li><a
|
||
href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html#onCapabilityChanged(com.google.android.gms.wearable.CapabilityInfo)">
|
||
{@code onCapabilityChanged()}</a>:
|
||
When a capability that an instance of your app advertises becomes available
|
||
on the network, that event triggers this callback. If you're looking for a
|
||
nearby node you can query the
|
||
<a
|
||
href="https://developers.google.com/android/reference/com/google/android/gms/wearable/Node.html#isNearby()">
|
||
{@code isNearby()}</a> method of the nodes provided in the callback.</li>
|
||
|
||
<p>
|
||
In addition to those on this list, you can listen for events from
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener">
|
||
{@code ChannelApi.ChannelListener}</a>, such as
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener.html#onChannelOpened(com.google.android.gms.wearable.Channel)">
|
||
{@code onChannelOpened()}</a>.
|
||
</p>
|
||
</ul>
|
||
<p>To create a <a
|
||
href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>, follow these steps:</p>
|
||
|
||
<ol>
|
||
<li>Create a class that extends
|
||
<a
|
||
href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
|
||
</li>
|
||
<li>Listen for the events that you're interested in, such as
|
||
<a
|
||
href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)">
|
||
<code>onDataChanged()</code></a>.
|
||
</li>
|
||
<li>Declare an intent filter in your Android manifest to notify the system
|
||
about your
|
||
<a
|
||
href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>
|
||
WearableListenerService</code></a>.
|
||
This declaration allows the system to bind your service as needed.
|
||
</li>
|
||
</ol>
|
||
|
||
<p>The following example shows how to implement a simple
|
||
<a
|
||
href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>
|
||
WearableListenerService</code></a>:
|
||
</p>
|
||
<pre>
|
||
public class DataLayerListenerService extends WearableListenerService {
|
||
|
||
private static final String TAG = "DataLayerSample";
|
||
private static final String START_ACTIVITY_PATH = "/start-activity";
|
||
private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";
|
||
|
||
@Override
|
||
public void onDataChanged(DataEventBuffer dataEvents) {
|
||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||
Log.d(TAG, "onDataChanged: " + dataEvents);
|
||
}
|
||
final List events = FreezableUtils
|
||
.freezeIterable(dataEvents);
|
||
|
||
GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
|
||
.addApi(Wearable.API)
|
||
.build();
|
||
|
||
ConnectionResult connectionResult =
|
||
googleApiClient.blockingConnect(30, TimeUnit.SECONDS);
|
||
|
||
if (!connectionResult.isSuccess()) {
|
||
Log.e(TAG, "Failed to connect to GoogleApiClient.");
|
||
return;
|
||
}
|
||
|
||
// Loop through the events and send a message
|
||
// to the node that created the data item.
|
||
for (DataEvent event : events) {
|
||
Uri uri = event.getDataItem().getUri();
|
||
|
||
// Get the node id from the host value of the URI
|
||
String nodeId = uri.getHost();
|
||
// Set the data of the message to be the bytes of the URI
|
||
byte[] payload = uri.toString().getBytes();
|
||
|
||
// Send the RPC
|
||
Wearable.MessageApi.sendMessage(googleApiClient, nodeId,
|
||
DATA_ITEM_RECEIVED_PATH, payload);
|
||
}
|
||
}
|
||
}
|
||
</pre>
|
||
|
||
<p>
|
||
The next section explains how to use an intent filter with this listener.
|
||
</p>
|
||
|
||
<h3>Using filters with WearableListenerService</h3>
|
||
|
||
<p>
|
||
An intent filter for the
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html">
|
||
{@code WearableListenerService}</a> example shown in the previous section might look like this:
|
||
|
||
<pre>
|
||
<service android:name=".DataLayerListenerService">
|
||
<intent-filter>
|
||
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
|
||
<data android:scheme="wear" android:host="*"
|
||
android:path="/start-activity" />
|
||
</intent-filter>
|
||
</service>
|
||
</pre>
|
||
|
||
<p>
|
||
In this filter, the {@code DATA_CHANGED} action replaces the
|
||
previously recommended {@code BIND_LISTENER} action so that only specific
|
||
events wake or launch your application. This change improves system efficiency
|
||
and reduces battery consumption and other overhead associated with your
|
||
application. In this example, the watch listens for the
|
||
{@code /start-activity} data item, and the
|
||
phone listens for the {@code /data-item-received} message response.
|
||
</p>
|
||
<p>
|
||
Standard Android filter matching rules apply. You can specify multiple services
|
||
per manifest, multiple intent filters per service, multiple actions per filter,
|
||
and multiple data stanzas per filter. Filters can match on a wildcard host or on
|
||
a specific one. To match on a wildcard host, use {@code host="*"}. To match
|
||
on a specific host, specify {@code host=<node_id>}.
|
||
</p>
|
||
|
||
<p>
|
||
You can also match a literal path or path prefix. If you are matching by path
|
||
or path prefix, you must specify a wildcard or specific host.
|
||
If you do not do so, the system ignores the path you specified.
|
||
</p>
|
||
|
||
<p>
|
||
For more information on the filter types that Wear supports, see the
|
||
API reference documentation for <a
|
||
href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService">
|
||
{@code WearableListenerService}</a>.
|
||
</p>
|
||
|
||
<p>
|
||
For more information on data filters and matching rules, see the API reference
|
||
documentation for the <a
|
||
href="{@docRoot}guide/topics/manifest/data-element.html">{@code data}</a>
|
||
manifest element.
|
||
</p>
|
||
|
||
<p>When matching intent filters, there are two important rules to remember:</p>
|
||
<ul>
|
||
<li>If a scheme is not specified for the intent filter, the system ignores
|
||
all the other URI attributes.</li>
|
||
<li>If no host is specified for the filter, the system ignores the
|
||
port attribute and all the path attributes.</li>
|
||
</ul>
|
||
|
||
<h3>With a listener activity</h3>
|
||
<p>
|
||
If your app only cares about data-layer events when the user is interacting
|
||
with the app, it may not need a long-running service to handle every data
|
||
change. In such a case, you can listen for events in an activity by
|
||
implementing one or more of the following interfaces:
|
||
</p>
|
||
<ul>
|
||
<li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>
|
||
DataApi.DataListener</code></a></li>
|
||
|
||
<li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.MessageListener.html">
|
||
<code>MessageApi.MessageListener</code></a></li>
|
||
|
||
<li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/CapabilityApi.CapabilityListener.html">{@code CapabilityApi.CapabilityListener}</a></li>
|
||
</ul>
|
||
|
||
<p>To create an activity that listens for data events:</p>
|
||
<ol>
|
||
<li>Implement the desired interfaces.</li>
|
||
<li>In {@link android.app.Activity#onCreate onCreate()}, create an instance of
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code>
|
||
</a>to work with the Data Layer API.</li>
|
||
|
||
<li>
|
||
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()">
|
||
<code>connect()</code></a> to connect the client to Google Play services.
|
||
</li>
|
||
|
||
<li>When the connection to Google Play services is established, the system calls
|
||
<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
|
||
|
||
<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)">
|
||
<code>DataApi.addListener()</code></a>,
|
||
|
||
<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)">
|
||
<code>MessageApi.addListener()</code></a>, or
|
||
|
||
<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)">
|
||
{@code CapabilityApi.addListener()}</a> to notify Google Play services that your activity is
|
||
interested in listening for data layer events.</li>
|
||
|
||
<li>In {@link android.app.Activity#onStop onStop()}, unregister any listeners with
|
||
<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>,
|
||
|
||
<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
|
||
|
||
<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)">
|
||
{@code CapabilityApi.removeListener()}</a>.</li>
|
||
|
||
|
||
<p>An alternative to adding listeners in
|
||
<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>
|
||
and removing them in
|
||
{@link android.app.Activity#onStop onStop()} is to add a filtered listener in an activity’s {@link android.app.Activity#onResume onResume()} and
|
||
remove it in {@link android.app.Activity#onPause onPause()}, so as to only receive data that is relevant to the
|
||
current application state.</p>
|
||
|
||
|
||
<li>Implement
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)">
|
||
<code>onDataChanged()</code></a>,
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.MessageListener.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)">
|
||
<code>onMessageReceived()</code></a>,
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html#onCapabilityChanged(com.google.android.gms.wearable.CapabilityInfo)">
|
||
{@code onCapabilityChanged()}</a>,
|
||
or methods from <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener.html">
|
||
Channel API listener methods</a>, depending on the interfaces that you implemented.</li>
|
||
</ol>
|
||
|
||
<p>Here's an example that implements
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>:</p>
|
||
|
||
<pre>
|
||
public class MainActivity extends Activity implements
|
||
DataApi.DataListener, ConnectionCallbacks, OnConnectionFailedListener {
|
||
|
||
private GoogleApiClient mGoogleApiClient;
|
||
|
||
@Override
|
||
protected void onCreate(Bundle savedInstanceState) {
|
||
super.onCreate(savedInstanceState);
|
||
|
||
setContentView(R.layout.main);
|
||
mGoogleApiClient = new GoogleApiClient.Builder(this)
|
||
.addApi(Wearable.API)
|
||
.addConnectionCallbacks(this)
|
||
.addOnConnectionFailedListener(this)
|
||
.build();
|
||
}
|
||
|
||
@Override
|
||
protected void onStart() {
|
||
super.onStart();
|
||
if (!mResolvingError) {
|
||
mGoogleApiClient.connect();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onConnected(Bundle connectionHint) {
|
||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||
Log.d(TAG, "Connected to Google Api Service");
|
||
}
|
||
Wearable.DataApi.addListener(mGoogleApiClient, this);
|
||
}
|
||
|
||
@Override
|
||
protected void onStop() {
|
||
if (null != mGoogleApiClient && mGoogleApiClient.isConnected()) {
|
||
Wearable.DataApi.removeListener(mGoogleApiClient, this);
|
||
mGoogleApiClient.disconnect();
|
||
}
|
||
super.onStop();
|
||
}
|
||
|
||
@Override
|
||
public void onDataChanged(DataEventBuffer dataEvents) {
|
||
for (DataEvent event : dataEvents) {
|
||
if (event.getType() == DataEvent.TYPE_DELETED) {
|
||
Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
|
||
} else if (event.getType() == DataEvent.TYPE_CHANGED) {
|
||
Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</pre>
|
||
<h3>Using Filters with Listener Activities</h3>
|
||
<p>
|
||
Just as you can specify intent filters for manifest-based
|
||
<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html">
|
||
<code>WearableListenerService</code></a> objects, you can also use intent filters when registering a
|
||
listener through the Wearable API. The same rules are applicable to both
|
||
API-based listeners manifest-based listeners.
|
||
</p>
|
||
|
||
<p>
|
||
A common pattern is to register a listener with a specific path or path prefix
|
||
in an activity’s {@link android.app.Activity#onResume onResume()} method, and to
|
||
remove the listener in the activity’s {@link android.app.Activity#onPause onPause()} method.
|
||
Implementing listeners in this fashion allows your application to more selectively receive events,
|
||
improving its design and efficiency.
|
||
</p>
|