Непредсказуемый результат DriveId.getResourceId() в Google Drive Android API
Проблема заключается в том, что "resourceID" из "DriveId.getResourceId()" недоступен (возвращает NULL) для вновь созданных файлов (продукт "DriveFolder.createFile(GAC, meta, cont)"). Если файл извлекается с помощью обычного списка или процедуры запроса, 'resourceID' является правильным.
Я подозреваю, что это проблема времени / задержки, но не ясно, есть ли какое-либо действие приложения, которое вызвало бы обновление. "Drive.DriveApi.requestSync(GAC)", похоже, не имеет никакого эффекта.
2 ответа
ОБНОВЛЕНИЕ (22.07.2015)
Благодаря быстрому ответу Стивена Базила (см. Комментарии ниже) у меня наконец-то есть удовлетворительное решение с использованием событий завершения. Вот два сокращенных фрагмента кода, которые доставляют 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;
}
}
}
При нормальных обстоятельствах (Wi-Fi), я получаю ResourceId почти сразу.
20:40:53.247﹕Created a empty file: DriveId:CAESABiiAiDGsfO61VMoAA==
20:40:54.305: onComplete, ResourceId: 0BxOS7mTBMR_bMHZRUjJ5NU1ZOWs
... сделано на данный момент.
ОРИГИНАЛЬНАЯ ПОЧТА, устарела, оставлена здесь для справки.
Я оставил этот ответ в течение года, надеясь, что GDAA разработает решение, которое работает. Причина моего ворчания проста. Если мое приложение создает файл, оно должно передать этот факт своим друзьям (например, другим устройствам) с значимым идентификатором (то есть ResourceId). Это простая задача в REST Api, где ResourceId возвращается, как только файл успешно создан.
Надо сказать, что я понимаю философию GDAA, заключающуюся в защите приложения от сетевых примитивов, кэширования, пакетной обработки... Но ясно, что в этой ситуации ResourceID доступен задолго до его доставки в приложение.
Первоначально я реализовал предложение Шерил Саймон и добавил ChangeListener во вновь созданный файл, надеясь получить ResourceID при распространении файла. Используя классическую CreateEmptyFileActivity из android-demos, я набросал следующий тестовый код:
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);
}
}
});
}
}
... и ждал, что что-то случится. Файл был успешно загружен на диск в течение нескольких секунд, но событие 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. Что поднимает следующий вопрос:
Почему я должен щекотать метаданные (или повторно фиксировать контент), очень вероятно создавая ненужный сетевой трафик, чтобы получить событие onChange(), когда я ясно вижу, что файл был распространен давно, и у GDAA есть ResourceId?
ResourceIds становятся доступными, когда вновь созданный ресурс фиксируется на сервере. В случае устройства, которое находится в автономном режиме, это может быть произвольно долго после первоначального создания файла. Это произойдет как можно скорее после запроса на создание, поэтому вам не нужно ничего делать, чтобы ускорить его.
Если вам это действительно нужно, вы можете использовать уведомления об изменениях для прослушивания изменения идентификатора ресурса.