Che*_*eng 58 android google-drive-api google-drive-android-api
目前,我正在使用Google Drive Android API将我的Android应用数据存储到Google云端硬盘应用文件夹.
这是我在保存应用程序数据时正在做的事情
这是执行上述操作的代码.
public static boolean saveToGoogleDrive(GoogleApiClient googleApiClient, File file, HandleStatusable h, PublishProgressable p) {
    // Should we new or replace?
    GoogleCloudFile googleCloudFile = searchFromGoogleDrive(googleApiClient, h, p);
    try {
        p.publishProgress(JStockApplication.instance().getString(R.string.uploading));
        final long checksum = org.yccheok.jstock.gui.Utils.getChecksum(file);
        final long date = new Date().getTime();
        final int version = org.yccheok.jstock.gui.Utils.getCloudFileVersionID();
        final String title = getGoogleDriveTitle(checksum, date, version);
        DriveContents driveContents;
        DriveFile driveFile = null;
        if (googleCloudFile == null) {
            DriveApi.DriveContentsResult driveContentsResult = Drive.DriveApi.newDriveContents(googleApiClient).await();
            if (driveContentsResult == null) {
                return false;
            }
            Status status = driveContentsResult.getStatus();
            if (!status.isSuccess()) {
                h.handleStatus(status);
                return false;
            }
            driveContents = driveContentsResult.getDriveContents();
        } else {
            driveFile = googleCloudFile.metadata.getDriveId().asDriveFile();
            DriveApi.DriveContentsResult driveContentsResult = driveFile.open(googleApiClient, DriveFile.MODE_WRITE_ONLY, null).await();
            if (driveContentsResult == null) {
                return false;
            }
            Status status = driveContentsResult.getStatus();
            if (!status.isSuccess()) {
                h.handleStatus(status);
                return false;
            }
            driveContents = driveContentsResult.getDriveContents();
        }
        OutputStream outputStream = driveContents.getOutputStream();
        InputStream inputStream = null;
        byte[] buf = new byte[8192];
        try {
            inputStream = new FileInputStream(file);
            int c;
            while ((c = inputStream.read(buf, 0, buf.length)) > 0) {
                outputStream.write(buf, 0, c);
            }
        } catch (IOException e) {
            Log.e(TAG, "", e);
            return false;
        } finally {
            org.yccheok.jstock.file.Utils.close(outputStream);
            org.yccheok.jstock.file.Utils.close(inputStream);
        }
        if (googleCloudFile == null) {
            // Create the metadata for the new file including title and MIME
            // type.
            MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
                    .setTitle(title)
                    .setMimeType("application/zip").build();
            DriveFolder driveFolder = Drive.DriveApi.getAppFolder(googleApiClient);
            DriveFolder.DriveFileResult driveFileResult = driveFolder.createFile(googleApiClient, metadataChangeSet, driveContents).await();
            if (driveFileResult == null) {
                return false;
            }
            Status status = driveFileResult.getStatus();
            if (!status.isSuccess()) {
                h.handleStatus(status);
                return false;
            }
        } else {
            MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
                    .setTitle(title).build();
            DriveResource.MetadataResult metadataResult = driveFile.updateMetadata(googleApiClient, metadataChangeSet).await();
            Status status = metadataResult.getStatus();
            if (!status.isSuccess()) {
                h.handleStatus(status);
                return false;
            }
        }
        Status status;
        try {
            status = driveContents.commit(googleApiClient, null).await();
        } catch (java.lang.IllegalStateException e) {
            // java.lang.IllegalStateException: DriveContents already closed.
            Log.e(TAG, "", e);
            return false;
        }
        if (!status.isSuccess()) {
            h.handleStatus(status);
            return false;
        }
        status = Drive.DriveApi.requestSync(googleApiClient).await();
        if (!status.isSuccess()) {
            // Sync request rate limit exceeded.
            //
            //h.handleStatus(status);
            //return false;
        }
        return true;
    } finally {
        if (googleCloudFile != null) {
            googleCloudFile.metadataBuffer.release();
        }
    }
}
private static String getGoogleDriveTitle(long checksum, long date, int version) {
    return "jstock-" + org.yccheok.jstock.gui.Utils.getJStockUUID() + "-checksum=" + checksum + "-date=" + date + "-version=" + version + ".zip";
}
// https://stackoverflow.com/questions/1360113/is-java-regex-thread-safe
private static final Pattern googleDocTitlePattern = Pattern.compile("jstock-" + org.yccheok.jstock.gui.Utils.getJStockUUID() + "-checksum=([0-9]+)-date=([0-9]+)-version=([0-9]+)\\.zip", Pattern.CASE_INSENSITIVE);
private static GoogleCloudFile searchFromGoogleDrive(GoogleApiClient googleApiClient, HandleStatusable h, PublishProgressable p) {
    DriveFolder driveFolder = Drive.DriveApi.getAppFolder(googleApiClient);
    // https://stackoverflow.com/questions/34705929/filters-ownedbyme-doesnt-work-in-drive-api-for-android-but-works-correctly-i
    final String titleName = ("jstock-" + org.yccheok.jstock.gui.Utils.getJStockUUID() + "-checksum=");
    Query query = new Query.Builder()
            .addFilter(Filters.and(
                Filters.contains(SearchableField.TITLE, titleName),
                Filters.eq(SearchableField.TRASHED, false)
            ))
            .build();
    DriveApi.MetadataBufferResult metadataBufferResult = driveFolder.queryChildren(googleApiClient, query).await();
    if (metadataBufferResult == null) {
        return null;
    }
    Status status = metadataBufferResult.getStatus();
    if (!status.isSuccess()) {
        h.handleStatus(status);
        return null;
    }
    MetadataBuffer metadataBuffer = null;
    boolean needToReleaseMetadataBuffer = true;
    try {
        metadataBuffer = metadataBufferResult.getMetadataBuffer();
        if (metadataBuffer != null ) {
            long checksum = 0;
            long date = 0;
            int version = 0;
            Metadata metadata = null;
            for (Metadata md : metadataBuffer) {
                if (p.isCancelled()) {
                    return null;
                }
                if (md == null || !md.isDataValid()) {
                    continue;
                }
                final String title = md.getTitle();
                // Retrieve checksum, date and version information from filename.
                final Matcher matcher = googleDocTitlePattern.matcher(title);
                String _checksum = null;
                String _date = null;
                String _version = null;
                if (matcher.find()){
                    if (matcher.groupCount() == 3) {
                        _checksum = matcher.group(1);
                        _date = matcher.group(2);
                        _version = matcher.group(3);
                    }
                }
                if (_checksum == null || _date == null || _version == null) {
                    continue;
                }
                try {
                    checksum = Long.parseLong(_checksum);
                    date = Long.parseLong(_date);
                    version = Integer.parseInt(_version);
                } catch (NumberFormatException ex) {
                    Log.e(TAG, "", ex);
                    continue;
                }
                metadata = md;
                break;
            }   // for
            if (metadata != null) {
                // Caller will be responsible to release the resource. If release too early,
                // metadata will not readable.
                needToReleaseMetadataBuffer = false;
                return GoogleCloudFile.newInstance(metadataBuffer, metadata, checksum, date, version);
            }
        }   // if
    } finally {
        if (needToReleaseMetadataBuffer) {
            if (metadataBuffer != null) {
                metadataBuffer.release();
            }
        }
    }
    return null;
}
在加载应用程序数据期间出现问题.想象一下以下操作
12345.正在使用的文件名是...checksum=12345...zip...checksum=12345...zip.下载内容.验证内容的校验和也是12345如此.67890.现有的应用程序文件夹zip文件已重命名为...checksum=67890...zip...checksum=67890...zip.但是,下载内容后,内容的校验和仍然是旧的12345!public static CloudFile loadFromGoogleDrive(GoogleApiClient googleApiClient, HandleStatusable h, PublishProgressable p) {
    final java.io.File directory = JStockApplication.instance().getExternalCacheDir();
    if (directory == null) {
        org.yccheok.jstock.gui.Utils.showLongToast(R.string.unable_to_access_external_storage);
        return null;
    }
    Status status = Drive.DriveApi.requestSync(googleApiClient).await();
    if (!status.isSuccess()) {
        // Sync request rate limit exceeded.
        //
        //h.handleStatus(status);
        //return null;
    }
    GoogleCloudFile googleCloudFile = searchFromGoogleDrive(googleApiClient, h, p);
    if (googleCloudFile == null) {
        return null;
    }
    try {
        DriveFile driveFile = googleCloudFile.metadata.getDriveId().asDriveFile();
        DriveApi.DriveContentsResult driveContentsResult = driveFile.open(googleApiClient, DriveFile.MODE_READ_ONLY, null).await();
        if (driveContentsResult == null) {
            return null;
        }
        status = driveContentsResult.getStatus();
        if (!status.isSuccess()) {
            h.handleStatus(status);
            return null;
        }
        final long checksum = googleCloudFile.checksum;
        final long date = googleCloudFile.date;
        final int version = googleCloudFile.version;
        p.publishProgress(JStockApplication.instance().getString(R.string.downloading));
        final DriveContents driveContents = driveContentsResult.getDriveContents();
        InputStream inputStream = null;
        java.io.File outputFile = null;
        OutputStream outputStream = null;
        try {
            inputStream = driveContents.getInputStream();
            outputFile = java.io.File.createTempFile(org.yccheok.jstock.gui.Utils.getJStockUUID(), ".zip", directory);
            outputFile.deleteOnExit();
            outputStream = new FileOutputStream(outputFile);
            int read = 0;
            byte[] bytes = new byte[1024];
            while ((read = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read);
            }
        } catch (IOException ex) {
            Log.e(TAG, "", ex);
        } finally {
            org.yccheok.jstock.file.Utils.close(outputStream);
            org.yccheok.jstock.file.Utils.close(inputStream);
            driveContents.discard(googleApiClient);
        }
        if (outputFile == null) {
            return null;
        }
        return CloudFile.newInstance(outputFile, checksum, date, version);
    } finally {
        googleCloudFile.metadataBuffer.release();
    }
}
首先,我想
Status status = Drive.DriveApi.requestSync(googleApiClient).await()
不能很好地完成这项工作.它在大多数情况下失败,出现错误信息Sync request rate limit exceeded.事实上,强加的硬限制requestSync,使得API不是特别有用 - Android Google Play/Drive Api
但是,即使requestSync成功,loadFromGoogleDrive仍然只能获取最新的文件名,但过时的校验和内容.
我100%肯定loadFromGoogleDrive会给我一个缓存的数据内容,并提供以下观察结果.
DownloadProgressListenerin driveFile.open,bytesDownloaded是0,bytesExpected是-1.loadFromGoogleDrive将能够获得具有正确校验和内容的最新文件名.有没有强大的方法,以避免始终从Google云端硬盘加载缓存的应用数据?
我设法制作一个演示.以下是重现此问题的步骤.
https://github.com/yccheok/google-drive-bug
文件名为"123.TXT"的文件,内容"123"将在app文件夹中创建.
之前的文件将重命名为"456.TXT",内容更新为"456"
找到文件名为"456.TXT"的文件,但读取先前缓存的内容"123".我期待内容"456".
请注意,如果我们
我已正式提交问题报告 - https://code.google.com/a/google.com/p/apps-api-issues/issues/detail?id=4727
这就是我的设备下的样子 - http://youtu.be/kuIHoi4A1c0
我意识到,并非所有用户都会遇到这个问题.例如,我曾使用另一款Nexus 6,Google Play Services 9.4.52(440-127739847)进行测试.问题没有出现.
我编译了一个APK用于测试目的 - https://github.com/yccheok/google-drive-bug/releases/download/1.0/demo.apk
| 归档时间: | 
 | 
| 查看次数: | 1783 次 | 
| 最近记录: |