2014-04-04 13 views
7

Das Problem ist, dass die 'resourceID' von 'DriveId.getResourceId()' nicht verfügbar ist (NULL zurück) auf neu erstellte Dateien (Produkt von 'DriveFolder.createFile (GAC, meta, cont) '). Wenn die Datei durch eine normale Liste oder Abfrage abgerufen wird, ist die 'ResourceID' korrekt.Unpredictable Ergebnis DriveId.getResourceId() in Google Drive Android API

Ich vermute, es ist ein Timing/Latenz-Problem, aber es ist nicht klar, ob es eine Anwendungsaktion gibt, die Aktualisierung erzwingen würde. Die 'Drive.DriveApi.requestSync (GAC)' scheint keine Wirkung zu haben.

Antwort

15

UPDATE (2015.07.22)
Dank der prompte Antwort von Steven Bazyl (siehe Kommentare unten), ich habe endlich eine befriedigende Lösung mit Completion Events. Hier sind zwei minified Code-Schnipsel, die die ResourceId an die App liefern, sobald die neu erstellte Datei auf dem Laufwerk propagiert wird:

Dateierstellung, fügen Veränderung Abonnement:

public class CreateEmptyFileActivity extends BaseDemoActivity { 
    private static final String TAG = "_X_"; 

    @Override 
    public void onConnected(Bundle connectionHint) { super.onConnected(connectionHint); 

    MetadataChangeSet meta = new MetadataChangeSet.Builder() 
     .setTitle("EmptyFile.txt").setMimeType("text/plain") 
     .build(); 

    Drive.DriveApi.getRootFolder(getGoogleApiClient()) 
     .createFile(getGoogleApiClient(), meta, null, 
     new ExecutionOptions.Builder() 
      .setNotifyOnCompletion(true) 
      .build() 
    ) 
     .setResultCallback(new ResultCallback<DriveFileResult>() { 
     @Override 
     public void onResult(DriveFileResult result) { 
      if (result.getStatus().isSuccess()) { 
      DriveId driveId = result.getDriveFile().getDriveId(); 
      Log.d(TAG, "Created a empty file: " + driveId); 
      DriveFile file = Drive.DriveApi.getFile(getGoogleApiClient(), driveId); 
      file.addChangeSubscription(getGoogleApiClient()); 
      } 
     } 
     }); 
    } 
} 

Event Service Fänge die Fertigstellung:

public class ChngeSvc extends DriveEventService { 
    private static final String TAG = "_X_"; 

    @Override 
    public void onCompletion(CompletionEvent event) { super.onCompletion(event); 
    DriveId driveId = event.getDriveId(); 
    Log.d(TAG, "onComplete: " + driveId.getResourceId()); 
    switch (event.getStatus()) { 
     case CompletionEvent.STATUS_CONFLICT: Log.d(TAG, "STATUS_CONFLICT"); event.dismiss(); break; 
     case CompletionEvent.STATUS_FAILURE: Log.d(TAG, "STATUS_FAILURE"); event.dismiss(); break; 
     case CompletionEvent.STATUS_SUCCESS: Log.d(TAG, "STATUS_SUCCESS "); event.dismiss(); break; 
    } 
    } 
} 

Unter normalen Umständen (wifi), erhalte ich die ResourceId fast sofort.

20:40:53.247﹕Created a empty file: DriveId:CAESABiiAiDGsfO61VMoAA== 
20:40:54.305: onComplete, ResourceId: 0BxOS7mTBMR_bMHZRUjJ5NU1ZOWs 

... für jetzt erledigt.

ORIGINAL POST, veraltet, hier als Referenz.

Ich lasse diese Antwort für ein Jahr sitzen in der Hoffnung, dass GDAA eine Lösung entwickeln wird, die funktioniert. Der Grund für mein Nörgeln ist einfach. Wenn meine App eine Datei erstellt, muss sie diese Tatsache an ihre Buddys (z. B. andere Geräte) mit einer ID senden, die aussagekräftig ist (also ResourceId). Es ist eine triviale Aufgabe unter der REST Api, wo ResourceId zurückkommt, sobald die Datei erfolgreich erstellt wurde.

Nadeln zu sagen, dass ich die GDAA-Philosophie der Abschirmung der App von Netzwerk-Primitiven, Caching, Batching verstehen, ... Aber klar, in dieser Situation ist die ResourceID verfügbar, lange bevor es an die App geliefert wird.

Ursprünglich setzte ich Cheryl Simons Vorschlag und fügte eine Change auf eine neu erstellte Datei, zu erhalten hofft die ResourceID, wenn die Datei ausbreitet.Mit klassischen CreateEmptyFileActivity von android-Demo, ich klatschte zusammen den folgenden Testcode:

public class CreateEmptyFileActivity extends BaseDemoActivity { 
    private static final String TAG = "CreateEmptyFileActivity"; 

    final private ChangeListener mChgeLstnr = new ChangeListener() { 
    @Override 
    public void onChange(ChangeEvent event) { 
     Log.d(TAG, "event: " + event + " resId: " + event.getDriveId().getResourceId()); 
    } 
    }; 


    @Override 
    public void onConnected(Bundle connectionHint) { super.onConnected(connectionHint); 

    MetadataChangeSet meta = new MetadataChangeSet.Builder() 
     .setTitle("EmptyFile.txt").setMimeType("text/plain") 
     .build(); 

    Drive.DriveApi.getRootFolder(getGoogleApiClient()) 
     .createFile(getGoogleApiClient(), meta, null) 
     .setResultCallback(new ResultCallback<DriveFileResult>() { 
     @Override 
     public void onResult(DriveFileResult result) { 
      if (result.getStatus().isSuccess()) { 
      DriveId driveId = result.getDriveFile().getDriveId(); 
      Log.d(TAG, "Created a empty file: " + driveId); 
      Drive.DriveApi.getFile(getGoogleApiClient(), driveId).addChangeListener(getGoogleApiClient(), mChgeLstnr); 
      } 
     } 
     }); 
    } 
} 

... und warte darauf, dass etwas passiert. Die Datei wurde innerhalb von Sekunden in die Drive hochgeladen, aber keine onChange() Ereignis. 10 Minuten, 20 Minuten, ... Ich konnte keinen Weg finden, den ChangeListener aufzuwecken.

Also die einzige andere Lösung, die ich heraufkommen könnte, war, den GDAA zu stupsen. So implementiert ich ein einfaches Handler-Poker, die die Metadaten kitzeln, bis etwas passiert:

public class CreateEmptyFileActivity extends BaseDemoActivity { 
    private static final String TAG = "CreateEmptyFileActivity"; 

    final private ChangeListener mChgeLstnr = new ChangeListener() { 
    @Override 
    public void onChange(ChangeEvent event) { 
     Log.d(TAG, "event: " + event + " resId: " + event.getDriveId().getResourceId()); 
    } 
    }; 

    static DriveId driveId; 
    private static final int ENOUGH = 4; // nudge 4x, 1+2+3+4 = 10seconds 
    private static int mWait = 1000; 
    private int mCnt; 
    private Handler mPoker; 
    private final Runnable mPoke = new Runnable() { public void run() { 
    if (mPoker != null && driveId != null && driveId.getResourceId() == null && (mCnt++ < ENOUGH)) { 
     MetadataChangeSet meta = new MetadataChangeSet.Builder().build(); 
     Drive.DriveApi.getFile(getGoogleApiClient(), driveId).updateMetadata(getGoogleApiClient(), meta).setResultCallback(
     new ResultCallback<DriveResource.MetadataResult>() { 
      @Override 
      public void onResult(DriveResource.MetadataResult result) { 
      if (result.getStatus().isSuccess() && result.getMetadata().getDriveId().getResourceId() != null) 
       Log.d(TAG, "resId COOL " + result.getMetadata().getDriveId().getResourceId()); 
      else 
       mPoker.postDelayed(mPoke, mWait *= 2); 
      } 
     } 
    ); 
    } else { 
     mPoker = null; 
    } 
    }}; 

    @Override 
    public void onConnected(Bundle connectionHint) { super.onConnected(connectionHint); 

    MetadataChangeSet meta = new MetadataChangeSet.Builder() 
     .setTitle("EmptyFile.txt").setMimeType("text/plain") 
     .build(); 

    Drive.DriveApi.getRootFolder(getGoogleApiClient()) 
     .createFile(getGoogleApiClient(), meta, null) 
     .setResultCallback(new ResultCallback<DriveFileResult>() { 
     @Override 
     public void onResult(DriveFileResult result) { 
      if (result.getStatus().isSuccess()) { 
      driveId = result.getDriveFile().getDriveId(); 
      Log.d(TAG, "Created a empty file: " + driveId); 
      Drive.DriveApi.getFile(getGoogleApiClient(), driveId).addChangeListener(getGoogleApiClient(), mChgeLstnr); 

      mCnt = 0; 
      mPoker = new Handler(); 
      mPoker.postDelayed(mPoke, mWait); 
      } 
     } 
     }); 
    } 
} 

Und voila, 4 Sekunden (mehr oder weniger) später die Change liefern eine neue glänzende ResourceId. Der ChangeListener wird damit natürlich überflüssig, da die Pokerroutine auch die ResourceId bekommt.

Das ist also die Antwort für diejenigen, die nicht auf die ResourceId warten können. Das bringt die Follow-up-Frage:

Warum muss ich Metadaten kitzeln (oder Inhalt wieder begehen), sehr wahrscheinlich unnötigen Netzwerkverkehr zu schaffen, onChange() Ereignis zu bekommen, wenn ich deutlich sehen, dass die Datei wurde vor langer Zeit verbreitet, und GDAA hat die ResourceId verfügbar?

+0

Für Ihren Anwendungsfall denke ich, dass Sie eher nach einem Abschlusshörer als nach einem Änderungshörer suchen. Die Ergebnisse, die Sie mit einem Change-Listener sehen, werden erwartet. Sie fügen den Änderungslistener erst nach dem Ergebnisrückruf hinzu * nachdem * Sie die Änderung vorgenommen haben. Also, bis zum * nächsten * Mal, an dem du die Datei kitzelst, wirst du keine Updates erhalten. Unter https://developers.google.com/drive/android/completion erfahren Sie, wie Sie benachrichtigt werden, wenn eine Änderung an den Server weitergegeben wurde. Das ist das Ereignis, an dem Sie interessiert sind, wenn Sie die zugrunde liegende Laufwerksdatei-ID abrufen möchten. –

+0

Danke, Entschuldigung, ich wusste nicht über "Vervollständigungen", es gab nicht, als ich dieses Problem ursprünglich hatte. Und seitdem bin ich im "REST Api-Universum" gewesen, also habe ich es verpasst. Werde es versuchen. – seanpj

9

ResourceIds werden verfügbar, wenn die neu erstellte Ressource für den Server festgeschrieben wird. Bei einem Gerät, das offline ist, könnte dies beliebig lange nach der anfänglichen Dateierstellung sein. Es wird jedoch so schnell wie möglich nach der Erstellungsanforderung geschehen, so dass Sie nichts tun müssen, um es zu beschleunigen.

Wenn Sie es wirklich sofort brauchen, könnten Sie möglicherweise die change notifications verwenden, um auf die Ressource ID zu warten, um zu ändern.

+0

Danke, auch wenn das nicht neu ist. Die meisten dieser hick-ups passieren, weil ich die RESTFul mit GDAA mischen muss. (Zum Beispiel erstelle ich eine Datei in GDAA und muss 'Beschreibung' über RESTful hochladen, aber keine resID herum). Der Code ist momentan ein Durcheinander, aber zumindest habe ich alle Grundlagen abgedeckt und da ihr immer Sachen hinzufügt, sieht es aus, als ob es von hier abwärts geht. – seanpj

+0

Wie viel Zeit braucht man zum Commit? – Nabin