【问题标题】:Unpredictable result of DriveId.getResourceId() in Google Drive Android APIGoogle Drive Android API 中 DriveId.getResourceId() 的不可预测的结果
【发布时间】:2014-04-04 23:27:04
【问题描述】:

问题是来自 'DriveId.getResourceId()' 的 'resourceID' 在新创建的文件('DriveFolder.createFile(GAC,元,续)')。如果通过常规列表或查询过程检索文件,则“resourceID”是正确的。

我怀疑这是一个时间/延迟问题,但不清楚是否存在强制刷新的应用程序操作。 'Drive.DriveApi.requestSync(GAC)' 似乎没有效果。

【问题讨论】:

    标签: google-drive-android-api


    【解决方案1】:

    更新(2015 年 7 月 22 日)
    感谢 Steven Bazyl 的及时响应(见下面的 cmets),我终于有了使用 Completion Events 的令人满意的解决方案。这是两个缩小的代码 sn-ps,它们在新创建的文件传播到驱动器后立即将 ResourceId 传递给应用程序:

    文件创建,添加变更订阅:

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

    事件服务,捕获完成:

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

    在正常情况下(wifi),我几乎立即得到ResourceId

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

    ...暂时完成。

    原始帖子,已弃用,留在这里供参考。

    我让这个答案搁置了一年,希望GDAA 能够开发出一个可行的解决方案。我唠叨的原因很简单。如果我的应用程序创建了一个文件,它需要使用有意义的 ID(即 ResourceId)将这一事实广播给它的伙伴(例如其他设备)。这是REST Api 下的一项简单任务,一旦文件创建成功,ResourceId 就会返回。

    需要说,我理解 GDAA 的理念,即保护应用免受网络原语、缓存、批处理......但显然,在这种情况下,ResourceID 在交付给应用之前很久就可用。

    原来我实现了Cheryl Simon的建议,在一个新创建的文件上添加了一个ChangeListener,希望在文件传播的时候得到ResourceID。使用来自 android-demos 的经典 CreateEmptyFileActivity,我将以下测试代码拼凑在一起:

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

    ... 并且正在等待某事发生。文件在几秒钟内愉快地上传到Drive,但没有onChange() 事件。 10 分钟,20 分钟,...我找不到任何方法让 ChangeListener 唤醒。

    因此,我能想到的唯一其他解决方案是推动 GDAA。因此,我实现了一个简单的处理程序扑克,它会处理元数据直到发生某些事情:

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

    瞧,4 秒(给予或接受)之后,ChangeListener 提供了一个新的闪亮 ResourceId。当然,ChangeListener 变得过时了,因为扑克程序也获得了 ResourceId。

    所以对于那些等不及 ResourceId 的人来说,这就是答案。这就引出了后续问题:

    当我清楚地看到文件已被传播到一个很久以前,GDAA 有 ResourceId 可用吗?

    【讨论】:

    • 对于您的用例,我认为您更多的是寻找完成侦听器而不是更改侦听器。您在更改侦听器中看到的结果是预期的。在结果回调之前,您不会添加更改侦听器,您进行了更改。所以,是的,在您下一次 对文件进行挠痒痒之前,您不会获得任何更新。请参阅developers.google.com/drive/android/completion,了解如何在更改已传播到服务器时获得通知。如果您想获取底层驱动器文件 ID,这就是您感兴趣的事件。
    • 谢谢,抱歉,当我最初遇到此问题时,我不知道“完成”不存在。从那以后我一直在“REST Api 世界”中,所以我错过了它。会试试的。
    【解决方案2】:

    当新创建的资源提交到服务器时,ResourceIds 变得可用。在设备离线的情况下,这可能在初始文件创建之后任意长时间。但它会在创建请求后尽快发生,因此您无需执行任何操作来加快速度。

    如果你真的需要它,你可以想象使用change notifications 来监听resourceId 的变化。

    【讨论】:

    • 谢谢,即使这不是新的。大多数这些问题都在发生,因为我必须将 RESTFul 与 GDAA 混合使用。 (例如,我在 GDAA 中创建了一个文件,并且必须通过 RESTful 上传“描述”,但周围没有 resID)。代码现在一团糟,但至少我已经涵盖了所有基础,并且随着你们不断添加东西,看起来它从这里开始走下坡路。
    • 提交需要多长时间?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-27
    • 1970-01-01
    • 2011-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多