2015-11-15 6 views
7

Ich möchte bidirektionale Datenübertragung zwischen Android Wear und Handheld machen. Alles scheint gut zu sein, außer onDataChanged auf Handheld auszulösen. Es löst nur dann aus, wenn ich ein USB-Kabel anschließe, das an den PC angeschlossen ist. Also ich verstehe nicht, warum es passiert ist.WearListenerService onDataChanged seltsames Verhalten

Hier ist mein Code:

WearListenerService auf Handheld:

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 auf Wear:

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"); 
         } 
        } 
       }); 


      } 
     } 
    } 
} 

Hauptaktivitäten sowohl auf Handheld und tragen Sie einfach Dienste starten. Datenpfad ist: 1) Tragen Sie den Service, um Daten zu senden. onDataChanged on wear ausgelöst, wie es sollte 2) Handheld Trigger onDataChanged nur nach dem Abstecken oder stecken USB-Kabel. Handheld sendet Daten zum Tragen. 3) onDataChanged Trigger auf Verschleiß wie es sollte und Daten empfangen.

Zusätzliche Informationen. 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> 

Manifest Wear:

<?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 Hand:

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 tragen:

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" 
} 

Log auf Handheld nach app starten:

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 = [email protected] [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] 

Log auf Verschleiß nach App starten und das Senden von Daten:

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 

Bitte zeigen Sie mir, was ich tue falsch?

Lösung

ich SendToDataLayerThread umschreiben. Jetzt ist es eine reguläre Klasse ohne Erweiterung Thread und DataRequest wurde dringend:

Danke für Hilfe!

Antwort

13

Per der Google Play services 8.3 blog post:

Mit Google Play-Dienste 8.3 haben wir die DataApi aktualisiert Dringlichkeit, wie Datenelemente zu ermöglichen, werden synchronisiert. Jetzt kann dem Datenelement eine Priorität hinzugefügt werden, um festzustellen, wann es synchronisiert werden sollte.Wenn Sie z. B. eine App erstellen, die eine sofortige Synchronisierung erfordert, z. B. eine Fernsteuerungs-App, können Sie dies dennoch sofort tun, indem Sie setUrgent() aufrufen. Bei einer Aktualisierung Ihrer Kontakte können Sie jedoch Verzögerungen tolerieren. Nicht dringende Datenelemente können sich um bis zu 30 Minuten verzögern, aber Sie können davon ausgehen, dass sie in den meisten Fällen innerhalb weniger Minuten zugestellt werden. Niedrige Priorität ist jetzt der Standardwert, daher wird setUrgent() benötigt, um das vorherige Timing zu erhalten.

So stellen Sie sicher, dass Sie setUrgent() auf Ihre Datenanforderungen aufrufen, wenn Sie möchten, dass sie sofort mit anderen verbundenen Geräten synchronisiert werden.

+1

Wow, danke dieses Problem auf Handheld zu lösen! (Ich füge das sowohl zum Handheld als auch zum Tragen hinzu). Jetzt löst es sofort onDataChange aus. Durch meinen Verschleiß ist es nach dem Senden von Daten an den Handheld ruhig. Ich versuche, Sendevorgang auf Handheld neu zu schreiben, wie es auf Verschleiß ist, aber nichts. –

+1

Jetzt habe ich still auf DataChanged auf Verschleiß für Handheld-Anfrage. Vom Protokoll (es ist wie fraglich) Ich sehe, dass Anfrage erfolgreich an das richtige Gerät gesendet wurde, aber onDataChanged auf Verschleiß erhält es nicht (egal mit oder ohne setUrgent()). Wenn ich von Handheld-Nachricht über MessageApi sende, wird es durch Verschleiss in hinzugefügt onMessageReceived empfangen. Ich möchte das Geheimnis wissen, warum MessageApi gut funktioniert und DataAPI irgendwo verloren hat. –

+0

Ich habe festgestellt, dass Daten in einer dieser Modifikationen verschleißen können: nach dem Senden von Daten machen googleapi disconnect() und connect() auf dem Handheld, oder ersetzen Thread.start() zu Thread.run() –