Google云端硬盘更改订阅无效?

时间:2015-11-10 19:56:12

标签: android google-play-services listener google-drive-android-api

我正在努力让'Change Subscriptions'使用适用于Android的Drive API,但到目前为止都没有成功。

这里是一个简单的用例:

  • 2个Android设备,均使用相同的Google帐户
  • 都在其驱动器文件夹中订阅了相同的“感兴趣的文件”
  • 如果文件“更改”,无论是由两个设备之一或任何外部源执行的更改,都会通知订阅此文件的所有设备

据我了解,这正是'Change Subscriptions'应该为我做的事情。我正在使用游戏服务修订版27。

我遇到的问题:

在一台设备上本地制作的“文件内容更改”(或其他一些文件事件)永远不会正确传播到订阅同一文件的所有其他设备。

有没有人知道这个问题的任何解决方案,或者可以指出我做错了什么?

我写了一些简单的测试代码(见下文),只需要连接googleApiClient,这就是我测试的内容:

1。

device 1创建一个名为testFileWriteNew()的新测试文件,并使用testFileAddAndRemoveSubscription()向该文件添加更改订阅,预期日志输出:

testfile.txt created, driveId=DriveId:CAESABi0AyDAu9XZhVMoAA== resourceId=null
onCompletion; driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYtAMgwLvV2YVTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
STATUS_SUCCESS 
added subscription to testfile.txt, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYtAMgwLvV2YVTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU

2

device 2使用testFileAddAndRemoveSubscription()添加对同一文件的更改订阅,即预期的日志输出:

added subscription to testfile.txt, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU

正如预期的那样,两个设备上的driveId都不同,但resourceId是相同的0B-sshen4iTFAN0htekFYNExuSEU,因此引用了相同的“云”文件

3

如果我通过testFileUpdate使用一些新数据更新文件,我会在device 1上获得以下内容:

testfile.txt updated, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYtAMgwLvV2YVTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU

device 2

testfile.txt updated, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU

4

不幸的是,服务的onChange方法中的“内容更改”仅在本地触发。 device 1完成的更改永远不会达到device 2,反之亦然。如果我使用device 2更新文件,我会看到来自该服务的device 2上的以下日志:

onChange; driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
contentChanged
onChange; driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
metadataChanged

但我从未看到onChange方法在device 1上被触发,如果device 2触发了我预期的更改。

代码:

private boolean testFileWriteNew() {
    final DriveFolder folderRoot = Drive.DriveApi.getRootFolder(mGoogleApiClient);
    DriveContentsResult contentsResult = Drive.DriveApi.newDriveContents(mGoogleApiClient).await();
    if (!contentsResult.getStatus().isSuccess()) {
        return false;
    }
    DriveContents originalContents = contentsResult.getDriveContents();
    OutputStream os = originalContents.getOutputStream();

    try {
        os.write(String.valueOf(System.currentTimeMillis()).getBytes());
        MetadataChangeSet originalMetadata = new MetadataChangeSet.Builder().setTitle("testfile.txt").setMimeType("text/plain").build();
        // create the file in root
        DriveFolder.DriveFileResult fileResult = folderRoot.createFile(mGoogleApiClient, originalMetadata, originalContents, new ExecutionOptions.Builder().setNotifyOnCompletion(true).build()).await();
        if (!fileResult.getStatus().isSuccess()) {
            return false;
        }
        // check 'locally created' file, not yet synced to drive
        DriveResource.MetadataResult metadataResult = fileResult.getDriveFile().getMetadata(mGoogleApiClient).await();
        if (!metadataResult.getStatus().isSuccess()) {
            return false;
        }
        Log.d(TAG, "testfile.txt created, driveId=" + metadataResult.getMetadata().getDriveId().encodeToString() + " resourceId=" + metadataResult.getMetadata().getDriveId().getResourceId());
        return true;
    } catch (IOException ioe) {
        return false;
    }
}


private boolean testFileUpdate() {
    final DriveFolder folderRoot = Drive.DriveApi.getRootFolder(mGoogleApiClient);

    // find testfile
    DriveId testFile = null;
    MetadataBufferResult folderFilesSyncFolder = folderRoot.listChildren(mGoogleApiClient).await();
    if (!folderFilesSyncFolder.getStatus().isSuccess()) {
        return false;
    } else {
        MetadataBuffer bufferMetaData = folderFilesSyncFolder.getMetadataBuffer();
        for(int i = 0; i < bufferMetaData.getCount(); ++i) {
            final Metadata data = bufferMetaData.get(i);
            if(!data.isFolder() && !data.isTrashed() && data.isEditable() && data.getTitle().equalsIgnoreCase("testfile.txt")) {
                testFile = data.getDriveId();
                break;
            }
        }
        bufferMetaData.release();
    }

    if(testFile == null) {
        return false;
    }

    // update testfile
    DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient, testFile);
    DriveContentsResult driveContentsResult = file.open(mGoogleApiClient, DriveFile.MODE_WRITE_ONLY, null).await();
    if (!driveContentsResult.getStatus().isSuccess()) {
        return false;
    }
    DriveContents originalContents = driveContentsResult.getDriveContents();
    OutputStream os = originalContents.getOutputStream();
    try {
        os.write(String.valueOf(System.currentTimeMillis()).getBytes());
        // commit changes
        com.google.android.gms.common.api.Status status = originalContents.commit(mGoogleApiClient, null).await();
        if(!status.isSuccess()) {
            return false;

        }
        Log.d(TAG, "testfile.txt updated, driveId=" + file.getDriveId().encodeToString() + " resourceId=" + file.getDriveId().getResourceId());
        return true;
    } catch (IOException ioe) {
        return false;
    }
}

private boolean testFileAddAndRemoveSubscription(boolean subscribe) {
    final DriveFolder folderRoot = Drive.DriveApi.getRootFolder(mGoogleApiClient);

    // find testfile
    DriveId testFile = null;
    MetadataBufferResult folderFilesSyncFolder = folderRoot.listChildren(mGoogleApiClient).await();
    if (!folderFilesSyncFolder.getStatus().isSuccess()) {
        return false;
    } else {
        MetadataBuffer bufferMetaData = folderFilesSyncFolder.getMetadataBuffer();
        for(int i = 0; i < bufferMetaData.getCount(); ++i) {
            final Metadata data = bufferMetaData.get(i);
            if(!data.isFolder() && !data.isTrashed() && data.isEditable() && data.getTitle().equalsIgnoreCase("testfile.txt")) {
                testFile = data.getDriveId();
                break;
            }
        }
        bufferMetaData.release();
    }

    if(testFile == null) {
        return false;
    }

    // subscribe & unsubscribe
    DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient, testFile);
    if(subscribe) {
        com.google.android.gms.common.api.Status status = file.addChangeSubscription(mGoogleApiClient).await();
        if(!status.isSuccess()) {
            return false;
        }
        Log.d(TAG, "added subscription to testfile.txt, driveId=" + file.getDriveId().encodeToString() + " resourceId=" + file.getDriveId().getResourceId());
        return true;
    } else {
        com.google.android.gms.common.api.Status status = file.removeChangeSubscription(mGoogleApiClient).await();
        if(!status.isSuccess()) {
            return false;
        }
        Log.d(TAG, "removed subscription from testfile.txt, driveId=" + file.getDriveId().encodeToString() + " resourceId=" + file.getDriveId().getResourceId());
        return true;
    }
}

这里是服务类:

public class ChangeService extends DriveEventService {

    // TAG
    private static final String TAG = ChangeService.class.getSimpleName();

    @Override
    public void onChange(ChangeEvent event) {
        final DriveId driveId = event.getDriveId();
        Log.e(TAG, "onChange; driveId=" + driveId.encodeToString() + " resourceId=" + driveId.getResourceId());
        if(event.hasContentChanged())       { Log.e(TAG, "contentChanged"); }
        else if(event.hasMetadataChanged()) { Log.e(TAG, "metadataChanged"); }
        else if(event.hasBeenDeleted())     { Log.e(TAG, "beenDeleted"); }
    }

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

}

1 个答案:

答案 0 :(得分:1)

我相信,你陷入了和我们之前许多人一样的陷阱。我最初也认为&#39; DriveEventService&#39;处理在同一帐户下运行的多个设备之间的通知。我悲惨地尝试和失败,see here(并注意到自2014年4月以来的响亮的沉默)。我总是只在一台设备上获取活动。所以,我实际上意识到Change Events仅在GooPlaySvcs实例中本地工作。

Steve Bazyl在this unrelated answer中的评论或多或少地证实了这一点(请阅读包括&#39; ORIGINAL POST&#39;段落),确认我的理论,即改变事件&# 39;和&#39;完成事件&#39;是本地的(完成事件报告网络操作的结果 - 如http响应)。

所以回答你的问题。经过一段时间的斗争后,我不得不制定一个不同的策略:

1 /执行GDAA动作(创建,更新)
2 /等待完成事件,表明您的mod已被提升为驱动器
3 /向注册参与者广播包含ResourceId(not DriveId!)和可选数据(最多4K)的GCM消息。
4 /&#39;注册参与者&#39;对消息做出反应并下载更新的元数据/内容,解决冲突。

此解决方案是从2014年夏季开始的,此后可能还有一些其他预先打包的解决方案。我很高兴听到有人知道是否有更优雅的解决方案。

坦率地说,如果完成事件没有及时反映(通知)来自其他设备的更新,我不明白thisthis是什么。

祝你好运