WearListenerService onDataChanged comportamiento extraño

Quiero hacer la transferencia bidireccional de datos entre Android Wear y Handheld. Todo parece ser bueno excepto desencadenando onDataChanged en Handheld. Se dispara sólo entonces enchufar \ fuera del cable USB, conectado a la PC. Así que no entiendo por qué sucede.

Aquí está mi código:

WearListenerService en la computadora de mano:

import android.content.Intent; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.wearable.DataApi; import com.google.android.gms.wearable.DataEvent; import com.google.android.gms.wearable.DataEventBuffer; import com.google.android.gms.wearable.DataMap; import com.google.android.gms.wearable.DataMapItem; import com.google.android.gms.wearable.Node; import com.google.android.gms.wearable.NodeApi; import com.google.android.gms.wearable.PutDataMapRequest; import com.google.android.gms.wearable.PutDataRequest; import com.google.android.gms.wearable.Wearable; import com.google.android.gms.wearable.WearableListenerService; import java.util.List; import ru.orangesoftware.financisto.db.DatabaseAdapter; import ru.orangesoftware.financisto.model.Category; import ru.orangesoftware.financisto.model.CategoryTree; import ru.orangesoftware.financisto.utils.Log; public class WearService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { private static final String WEARABLE_DATA_PATH = "/wearable_data"; private static final String HANDHELD_DATA_PATH = "/handheld_data"; private SendToDataLayerThread s; GoogleApiClient googleClient; private DatabaseAdapter db; @Override public void onCreate() { super.onCreate(); Log.d("WearService Created"); db = new DatabaseAdapter(this); db.open(); initGoogleApiClient(); } @Override public void onDataChanged(DataEventBuffer dataEvents) { Log.d("In dataChanged"); DataMap dataMap; for (DataEvent event : dataEvents) { // Check the data type if (event.getType() == DataEvent.TYPE_CHANGED) { // Check the data path String path = event.getDataItem().getUri().getPath(); if (path.equals(HANDHELD_DATA_PATH)) { dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); Log.v("Path phone: " + path); Log.v("DataMap received from watch: " + dataMap); Intent messageIntent = new Intent(); messageIntent.setAction(Intent.ACTION_SEND); messageIntent.putExtra("time", System.currentTimeMillis()); messageIntent.putExtra("DataMap", dataMap.toBundle()); LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); // Create a DataMap object and send it to the data layer dataMap = new DataMap(); dataMap.putString("Pong", "Pong" + String.valueOf(System.currentTimeMillis())); dataMap.putLong("time", System.currentTimeMillis()); //Requires a new thread to avoid blocking the UI s = new SendToDataLayerThread(WEARABLE_DATA_PATH, dataMap); s.start(); } } } } private void initGoogleApiClient() { // Build a new GoogleApiClient for the the Wearable API Log.d("Initialaizing GoogleClient"); if (googleClient == null) { googleClient = new GoogleApiClient.Builder(this) .addApi(Wearable.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); } if (!googleClient.isConnected()) { Log.d("Tring to connect to GoogleApi..."); googleClient.connect(); } Log.d("Google Client ID = " + googleClient.toString()); } // Disconnect from the data layer when the Activity stops @Override public void onDestroy() { super.onDestroy(); Log.d("WearService: onDestroy"); if (null != googleClient && googleClient.isConnected()) { googleClient.disconnect(); } if (db != null) { db.close(); } } @Override public void onConnected(Bundle bundle) { Log.d("onConnected entered"); Log.d("GoogleAPI now status:" + googleClient.isConnected()); } @Override public void onConnectionSuspended(int i) { } @Override public void onConnectionFailed(ConnectionResult result) { Log.e("Connection to google api has failed. " + result.getErrorMessage()); } class SendToDataLayerThread extends Thread { String path; DataMap dataMap; // Constructor for sending data objects to the data layer SendToDataLayerThread(String p, DataMap data) { path = p; dataMap = data; } public void run() { NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleClient).await(); for (Node node : nodes.getNodes()) { // Construct a DataRequest and send over the data layer PutDataMapRequest putDMR = PutDataMapRequest.create(path); putDMR.getDataMap().putAll(dataMap); PutDataRequest request = putDMR.asPutDataRequest(); DataApi.DataItemResult result = Wearable.DataApi.putDataItem(googleClient, request).await(); if (result.getStatus().isSuccess()) { Log.v("DataMap: " + dataMap + " sent to: " + node.getDisplayName() + "; path=" + path); } else { // Log an error Log.v("ERROR: failed to send DataMap"); } } } } } 

WearListenerService en el desgaste :

 import android.content.Intent; import android.os.Bundle; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.wearable.DataApi; import com.google.android.gms.wearable.DataEvent; import com.google.android.gms.wearable.DataEventBuffer; import com.google.android.gms.wearable.DataMap; import com.google.android.gms.wearable.DataMapItem; import com.google.android.gms.wearable.Node; import com.google.android.gms.wearable.NodeApi; import com.google.android.gms.wearable.PutDataMapRequest; import com.google.android.gms.wearable.PutDataRequest; import com.google.android.gms.wearable.Wearable; import com.google.android.gms.wearable.WearableListenerService; public class ListenerService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { private static final String WEARABLE_DATA_PATH = "/wearable_data"; private static final String HANDHELD_DATA_PATH = "/handheld_data"; GoogleApiClient googleClient; private SendToDataLayerThread s; @Override public void onDataChanged(DataEventBuffer dataEvents) { Log.d("In dataChanged"); DataMap dataMap; for (DataEvent event : dataEvents) { // Check the data type if (event.getType() == DataEvent.TYPE_CHANGED) { // Check the data path String path = event.getDataItem().getUri().getPath(); Log.d("DataChanged: " + "path = " + path); if (path.equals(WEARABLE_DATA_PATH)) { dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); Log.d("DataChanged: " + "dataMap received on watch: " + dataMap); } } } } private void initGoogleApiClient() { if (googleClient == null) { Log.d("Building google client id..."); googleClient = new GoogleApiClient.Builder(this) .addApi(Wearable.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); Log.d("Google client id = " + googleClient.toString()); } if (!googleClient.isConnected()) { googleClient.connect(); } Log.d("Google Client ID = " + googleClient.toString()); } // Placeholders for required connection callbacks @Override public void onConnectionSuspended(int cause) { } @Override public void onConnected(Bundle connectionHint) { Log.v("OnConnected entered"); } @Override public void onConnectionFailed(ConnectionResult connectionResult) { } // Connect to the data layer when the Activity starts @Override public void onCreate() { super.onCreate(); initGoogleApiClient(); } // Disconnect from the data layer when the Activity stops @Override public void onDestroy() { if (null != googleClient && googleClient.isConnected()) { Log.d("onDestroy: Disconnecting googleClient"); googleClient.disconnect(); } super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startid) { Log.d("onStartCommand: Service was started."); // Create a DataMap object and send it to the data layer DataMap dataMap = new DataMap(); dataMap.putString("ping", "ping" + String.valueOf(System.currentTimeMillis())); dataMap.putLong("time", System.currentTimeMillis()); //Requires a new thread to avoid blocking the UI s = new SendToDataLayerThread(HANDHELD_DATA_PATH, dataMap); s.start(); return super.onStartCommand(intent, flags, startid); } class SendToDataLayerThread extends Thread { String path; DataMap dataMap; // Constructor for sending data objects to the data layer SendToDataLayerThread(String p, DataMap data) { path = p; dataMap = data; } public void run() { NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleClient).await(); for (Node node : nodes.getNodes()) { final Node node2 = node; // Construct a DataRequest and send over the data layer PutDataMapRequest putDMR = PutDataMapRequest.create(path); putDMR.getDataMap().putAll(dataMap); PutDataRequest request = putDMR.asPutDataRequest(); PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(googleClient, request); pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() { @Override public void onResult(DataApi.DataItemResult dataItemResult) { if (dataItemResult.getStatus().isSuccess()) { Log.v("DataMap: " + dataMap + " sent to: " + node2.getDisplayName()); } else { // Log an error Log.v("ERROR: failed to send DataMap"); } } }); } } } } 

Las actividades principales de Handheld y Wear solo empiezan los servicios. La trayectoria de datos es: 1) Utilice datos de envío de servicio. OnDataChanged en el desgaste desencadenado como debería 2) Accionadores de mano onDataChanged sólo después de desenchufar o conectar el cable USB. La computadora de mano envía los datos al desgaste. 3) onDataChanged desencadena el desgaste como debería y recibe datos.

Información adicional. Manifest Handheld:

 <?xml version="1.0" encoding="utf-8"?> <manifest package="ru.orangesoftware.financisto" xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly"> <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true"/> <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.location" android:required="false"/> <uses-feature android:name="android.hardware.location.network" android:required="false"/> <uses-feature android:name="android.hardware.location.gps" android:required="false"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.GET_ACCOUNTS"/> <uses-permission android:name="android.permission.USE_CREDENTIALS"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <application android:allowBackup="true" android:description="@string/app_description" android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault"> <uses-library android:name="com.google.android.maps" android:required="false"/> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/> <activity android:name=".activity.MainActivity" android:configChanges="orientation|keyboardHidden" android:label="@string/app_name" android:taskAffinity=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <service android:name=".service.WearService"> <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER"/> </intent-filter> </service> </application> </manifest> 

Desgaste manifiesto:

 <?xml version="1.0" encoding="utf-8"?> <manifest package="ru.orangesoftware.financisto" xmlns:android="http://schemas.android.com/apk/res/android"> <uses-feature android:name="android.hardware.type.watch"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@android:style/Theme.DeviceDefault"> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/> <activity android:name=".MainWearActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <service android:name="ru.orangesoftware.financisto.ListenerService" android:enabled="true"> <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER"/> </intent-filter> </service> </application> </manifest> 

Gradle Handheld :

 buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:1.3.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } } apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' repositories { maven { url "http://repo.commonsware.com.s3.amazonaws.com" } maven { url "https://repository-achartengine.forge.cloudbees.com/snapshot/" } mavenCentral() } android { compileSdkVersion 22 buildToolsVersion "23.0.2" defaultConfig { applicationId "ru.orangesoftware.financisto" minSdkVersion 19 targetSdkVersion 22 versionCode 92 versionName "1.6.8" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/LICENSE.txt' exclude 'META-INF/NOTICE' exclude 'META-INF/NOTICE.txt' } } def googlePlayVersion = '8.3.0' dependencies { compile "com.google.android.gms:play-services-base:$googlePlayVersion" compile "com.google.android.gms:play-services-drive:$googlePlayVersion" compile "com.google.android.gms:play-services-wearable:$googlePlayVersion" compile files('libs/dropbox-android-sdk-1.6.1/dropbox-android-sdk-1.6.1.jar') compile files('libs/google-rfc-2445/rfc2445-no-joda.jar') compile 'com.google.code.gson:gson:2.3' compile 'com.commonsware.cwac:wakeful:1.0.1' compile 'org.achartengine:achartengine:1.2.0' compile 'net.sf.trove4j:trove4j:3.0.3' compile 'com.wdullaer:materialdatetimepicker:2.0.0' } 

Gradle Wear :

 apply plugin: 'com.android.application' android { compileSdkVersion 22 buildToolsVersion "23.0.2" defaultConfig { applicationId "ru.orangesoftware.financisto" minSdkVersion 20 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } def googlePlayVersion = '8.3.0' dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.google.android.support:wearable:1.3.0' compile "com.google.android.gms:play-services-wearable:$googlePlayVersion" } 

Iniciar sesión en la computadora de mano después del inicio de la aplicación :

 11-15 12:20:20.616 29043-29043/? D/Financisto: WearService Created [WearService.onCreate:44] 11-15 12:20:20.616 29043-29043/? D/Financisto: Initialaizing GoogleClient [WearService.initGoogleApiClient:94] 11-15 12:20:20.636 29043-29043/? D/Financisto: Tring to connect to GoogleApi... [WearService.initGoogleApiClient:107] 11-15 12:20:20.636 29043-29043/? D/Financisto: Google Client ID = com.google.android.gms.internal.zzmg@4344d5c0 [WearService.initGoogleApiClient:113] 11-15 12:20:21.016 29043-29043/ru.orangesoftware.financisto D/Financisto: onConnected entered [WearService.onConnected:139] 11-15 12:20:21.016 29043-29043/ru.orangesoftware.financisto D/Financisto: GoogleAPI now status:true [WearService.onConnected:140] -------now I send data from watch and unplug usb cable after 30 seconds 11-15 12:24:31.986 29043-29091/? D/Financisto: In dataChanged [WearService.onDataChanged:54] 11-15 12:24:31.986 29043-29091/? V/Financisto: Path phone: /handheld_data [WearService.onDataChanged:68] 11-15 12:24:31.986 29043-29091/? V/Financisto: DataMap received from watch: {time=1447565065308, ping=ping1447565065306} [WearService.onDataChanged:69] 11-15 12:24:32.036 29043-29091/? D/Financisto: In dataChanged [WearService.onDataChanged:54] 11-15 12:24:32.046 29043-1493/? V/Financisto: DataMap: {Pong=Pong1447565071992, time=1447565071992} sent to: Gear 2 76A1; path=/wearable_data [SendToDataLayerThread.run:179] 

Iniciar sesión Desgaste después del inicio de la aplicación y envío de datos:

 11-15 12:24:25.301 2460-2460/ru.orangesoftware.financisto D/FinancistoWear: onStartCommand: Service was started. 11-15 12:24:25.377 2460-2460/ru.orangesoftware.financisto V/FinancistoWear: DataMap: {time=1447565065308, ping=ping1447565065306} sent to: Tolive GN3 11-15 12:24:25.379 2460-3309/ru.orangesoftware.financisto D/FinancistoWear: In dataChanged 11-15 12:24:25.379 2460-3309/ru.orangesoftware.financisto D/FinancistoWear: DataChanged: path = /handheld_data 

Por favor, señalame, ¿qué estoy haciendo mal?

Solución

Reescribo SendToDataLayerThread. Ahora es una clase regular sin extender Thread y DataRequest se hizo urgente:

 class SendToDataLayerThread { String path; DataMap dataMap; // Constructor for sending data objects to the data layer SendToDataLayerThread(String p, DataMap data) { path = p; dataMap = data; } public void run() { //NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleClient); PendingResult<NodeApi.GetConnectedNodesResult> nodes = Wearable.NodeApi.getConnectedNodes(googleClient); nodes.setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() { @Override public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) { for (Node node : getConnectedNodesResult.getNodes()) { final Node node2 = node; // Construct a DataRequest and send over the data layer PutDataMapRequest putDMR = PutDataMapRequest.create(path); putDMR.getDataMap().putAll(dataMap); PutDataRequest request = putDMR.asPutDataRequest(); request.setUrgent(); PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(googleClient, request); pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() { @Override public void onResult(DataApi.DataItemResult dataItemResult) { if (dataItemResult.getStatus().isSuccess()) { Log.v("DataMap: " + dataMap + " sent to: " + node2.getDisplayName()); } else { // Log an error Log.v("ERROR: failed to send DataMap"); } } }); } } }); } } 

¡Gracias por la ayuda!

Según la publicación de Google Play Services 8.3 :

Con Google Play Services 8.3, hemos actualizado DataApi para permitir la urgencia en la sincronización de los elementos de datos. Ahora, se puede agregar una prioridad al elemento de datos para determinar cuándo debe sincronizarse. Por ejemplo, si está creando una aplicación que requiera sincronización inmediata, como una aplicación de control remoto, todavía se puede hacer inmediatamente llamando a setUrgent (), pero para algo como la actualización de sus contactos, podría tolerar algún retraso. Los DataItems no urgentes pueden retrasarse hasta 30 minutos , pero se puede esperar que en la mayoría de los casos se entreguen en pocos minutos. La prioridad baja ahora es la predeterminada , por lo que se necesita setUrgent () para obtener la temporización anterior.

Por lo tanto, asegúrese de llamar a setUrgent () en sus solicitudes de datos si desea que se sincronicen inmediatamente con otros dispositivos conectados.

  • Cómo implementar el reconocimiento de gestos en el uso de Android
  • Imagen de fondo para la notificación de desgaste androide
  • DataAPI mucho más lento que MessageAPI?
  • Android Wear y Google Fit: ¿Cómo forzar la actualización de datasets entre smartwatch y smartphone?
  • Android OpenGLRenderer error "sucio"
  • BoxInsetLayout no funciona
  • Wearable no instalar la aplicación (criterios de funcionalidad de Android Wear)
  • ¿Cómo desactiva Android Wear cuando está en modo de suspensión?
  • ¿Soporta Android Wear HttpURLConnection - obtener EOFException
  • Cómo empaquetar una app de WatchFace sólo para la publicación de Google Play
  • ¿Cuál es el Uri para Wearable.DataApi.getDataItem () después de usar PutDataMapRequest?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.