【问题标题】:Using Google Drive to backup and restore SQLite Database使用 Google Drive 备份和恢复 SQLite 数据库
【发布时间】:2015-04-24 16:59:00
【问题描述】:

我设法在 SD 卡上创建了我的数据库备份并从那里恢复,但我意识到我备份的目的是确保数据的安全,在这种情况下,如果物理设备本身损坏, SD 卡上的备份丢失或自燃。因此,在这种情况下,将备份放在与原始位置相同的位置,坦率地说,这违背了备份的目的。

所以我想使用 Google Drive 作为保存 db 文件的更安全的地方,而且它是免费的。我查看了 Google 的 quickstart 演示,我工作得很好。但我仍然不知道如何为我的案子完成这项工作。

我发现了一些代码可以摆弄,但它仍在使用一些不推荐使用的方法,到目前为止,我只在省略不推荐使用的区域时才设法运行它,但它只会在我的 Google Drive 中创建一个空白二进制文件,所以我认为该弃用区域是它实际上传数据库备份内容的地方。如果有人可以提供帮助,将不胜感激。

我会把它留在下面,以防有人可以用它来更好地向我解释事情。我还在下面标记了不推荐使用的方法,它已接近尾声。

public class ExpectoPatronum extends Activity implements ConnectionCallbacks, OnConnectionFailedListener {

private static final String TAG = "MainActivity";
private GoogleApiClient api;
private boolean mResolvingError = false;
private DriveFile mfile;
private static final int DIALOG_ERROR_CODE =100;
private static final String DATABASE_NAME = "demodb";
private static final String GOOGLE_DRIVE_FILE_NAME = "sqlite_db_backup";

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Create the Drive API instance
    api = new GoogleApiClient.Builder(this).addApi(Drive.API).addScope(Drive.SCOPE_FILE).
            addConnectionCallbacks(this).addOnConnectionFailedListener(this).build();
}

@Override
public void onStart() {
    super.onStart();
    if(!mResolvingError) {
        api.connect(); // Connect the client to Google Drive
    }
}

@Override
public void onStop() {
    super.onStop();
    api.disconnect(); // Disconnect the client from Google Drive
}

@Override
public void onConnectionFailed(ConnectionResult result) {
    Log.v(TAG, "Connection failed");
    if(mResolvingError) { // If already in resolution state, just return.
        return;
    } else if(result.hasResolution()) { // Error can be resolved by starting an intent with user interaction
        mResolvingError = true;
        try {
            result.startResolutionForResult(this, DIALOG_ERROR_CODE);
        } catch (SendIntentException e) {
            e.printStackTrace();
        }
    } else { // Error cannot be resolved. Display Error Dialog stating the reason if possible.
        ErrorDialogFragment fragment = new ErrorDialogFragment();
        Bundle args = new Bundle();
        args.putInt("error", result.getErrorCode());
        fragment.setArguments(args);
        fragment.show(getFragmentManager(), "errordialog");
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if(requestCode == DIALOG_ERROR_CODE) {
        mResolvingError = false;
        if(resultCode == RESULT_OK) { // Error was resolved, now connect to the client if not done so.
            if(!api.isConnecting() && !api.isConnected()) {
                api.connect();
            }
        }
    }
}

@Override
public void onConnected(Bundle connectionHint) {
    Log.v(TAG, "Connected successfully");

    /* Connection to Google Drive established. Now request for Contents instance, which can be used to provide file contents.
       The callback is registered for the same. */
    Drive.DriveApi.newDriveContents(api).setResultCallback(contentsCallback);
}

final private ResultCallback<DriveApi.DriveContentsResult> contentsCallback = new ResultCallback<DriveApi.DriveContentsResult>() {

    @Override
    public void onResult(DriveApi.DriveContentsResult result) {
        if (!result.getStatus().isSuccess()) {
            Log.v(TAG, "Error while trying to create new file contents");
            return;
        }

        String mimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType("db");
        MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
                .setTitle(GOOGLE_DRIVE_FILE_NAME) // Google Drive File name
                .setMimeType(mimeType)
                .setStarred(true).build();
        // create a file on root folder
        Drive.DriveApi.getRootFolder(api)
                .createFile(api, changeSet, result.getDriveContents())
                .setResultCallback(fileCallback);
    }

};

final private ResultCallback<DriveFileResult> fileCallback = new ResultCallback<DriveFileResult>() {

    @Override
    public void onResult(DriveFileResult result) {
        if (!result.getStatus().isSuccess()) {
            Log.v(TAG, "Error while trying to create the file");
            return;
        }
        mfile = result.getDriveFile();
        mfile.open(api, DriveFile.MODE_WRITE_ONLY, null).setResultCallback(contentsOpenedCallback);
    }
};

final private ResultCallback<DriveApi.DriveContentsResult> contentsOpenedCallback = new ResultCallback<DriveApi.DriveContentsResult>() {

    @Override
    public void onResult(DriveApi.DriveContentsResult result) {

        if (!result.getStatus().isSuccess()) {
            Log.v(TAG, "Error opening file");
            return;
        }

        try {
            FileInputStream is = new FileInputStream(getDbPath());
            BufferedInputStream in = new BufferedInputStream(is);
            byte[] buffer = new byte[8 * 1024];
            DriveContents content = result.getDriveContents();
            BufferedOutputStream out = new BufferedOutputStream(content.getOutputStream());
            int n = 0;
            while( ( n = in.read(buffer) ) > 0 ) {
                out.write(buffer, 0, n);
            }

            in.close();
commitAndCloseContents is DEPRECATED -->/**mfile.commitAndCloseContents(api, content).setResultCallback(new ResultCallback<Status>() {   
                @Override
                public void onResult(Status result) {
                    // Handle the response status
                }
            });**/
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

};

private File getDbPath() {
    return this.getDatabasePath(DATABASE_NAME);
}

@Override
public void onConnectionSuspended(int cause) {
    // TODO Auto-generated method stub
    Log.v(TAG, "Connection suspended");

}

public void onDialogDismissed() {
    mResolvingError = false;
}

public static class ErrorDialogFragment extends DialogFragment {
    public ErrorDialogFragment() {}

    public Dialog onCreateDialog(Bundle savedInstanceState) {
        int errorCode = this.getArguments().getInt("error");
        return GooglePlayServicesUtil.getErrorDialog(errorCode, this.getActivity(), DIALOG_ERROR_CODE);
    }

    public void onDismiss(DialogInterface dialog) {
        ((ExpectoPatronum) getActivity()).onDialogDismissed();
    }
}
}

【问题讨论】:

  • 确保您了解 Drive rest API 和 GDAA 之间的区别。对于您的用例,gdaa 是最好的方法,因此您应该忽略任何不是 gdaa 的快速入门教程。顺便说一句,将这么多代码发布到一个问题中并不是很有用。

标签: android google-drive-api google-drive-android-api


【解决方案1】:

用于访问 Google Drive 的两个 API 都处理二进制内容。所以你唯一需要做的就是上传你的二进制数据库文件,给它一个正确的 MIME 类型和一个 NAME(标题)。
API 的选择取决于你,GDAA 表现得像一个“本地”实体,上传/下载由 Google Play 服务处理,REST Api 更底层,给你更多控制权,但你必须照顾网络问题(wifi 开/关等),即您通常必须构建同步服务才能这样做。 GDAA 由 GooPlaySvcs 为您完成。但我离题了。
我可以向您指出这个GitHub demo,它是相当新的(GooPlaySvcs 7.00.+),我用来测试不同的 REST / GDAA 问题。 MainActivity 有点复杂,因为它允许在不同的 Google 帐户之间切换,但如果您通过 these hurdles,您可以使用 REST 或 GDAA CRUD 包装器。

看看this line。 byte[] 缓冲区包含二进制 JPEG 数据,它带有“image/jpeg”mime 类型(和基于时间的名称)。唯一需要做的就是使用如下结构将 DB 文件加载到 byte[] 缓冲区中:

  private static final int BUF_SZ = 4096;

  static byte[] file2Bytes(File file) {
    if (file != null) try {
       return is2Bytes(new FileInputStream(file));
    } catch (Exception ignore) {}
   return null;
  }

  static byte[] is2Bytes(InputStream is) {
    byte[] buf = null;
    BufferedInputStream bufIS = null;
    if (is != null) try {
      ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
      bufIS = new BufferedInputStream(is);
      buf = new byte[BUF_SZ];
      int cnt;
      while ((cnt = bufIS.read(buf)) >= 0) {
        byteBuffer.write(buf, 0, cnt);
      }
      buf = byteBuffer.size() > 0 ? byteBuffer.toByteArray() : null;
    } catch (Exception e) {le(e);}
    finally {
      try {
        if (bufIS != null) bufIS.close();
      } catch (Exception e) {le(e);}
    }
    return buf;
  }

我现在不记得 SQLite DB 的 MIME 类型了,但我确信它可以完成,因为我曾经做过一次(不幸的是,代码现在已经消失了)。而且我记得我实际上可以使用一些网络应用程序“在云端”访问和修改 SQLite DB。

祝你好运

更新:
在我写完上面的咆哮之后,我看了你正在谈论的演示。如果你有它的工作,最简单的方法实际上是插入你的数据库文件here,设置正确的 MIME,你很高兴。任君挑选。
并解决您的“已弃用”问题。 GDAA 仍在开发中,快速入门已经有一年多了。这就是我们生活的世界:-)

【讨论】:

  • 感谢您的链接和解释,他们解决了我的一些问题。决定接受您的建议,只是努力删除“拍照”部分并用其他东西替换它。我尝试处理另一个,但到目前为止仍然得到一个空的二进制文件。我会在其他时间解决这个问题。现在我只是从驱动文件恢复数据库,你不会碰巧有任何指针吗?
  • 你应该关注的核心在这里:github.com/seanpjanson/GDAADemo/blob/master/src/main/java/com/…。修改方法使其适合您的环境并逐步检查“buf”(注意它是 buf.length)。最后,您应该在 GooDrive 中看到该文件,并且它的长度应该相同。尝试将其下载到您的桌面,并与您的 Android 设备上的原始版本进行比较。 GDAA 不关心您要发送什么样的文件,它只是将字节向上移动,创建一个对象并附加元数据(标题、mime、描述......)
  • @seanpj 链接好像坏了!!能不能更新一下!
  • @shadygoneinsane。不幸的是,我已经退休一年多了。唯一可能有帮助(并且仍然有效)的代码可以在这里找到:github.com/seanpjanson/GDAADemo 和这里github.com/seanpjanson/RESTDemo。但它可能已经过时了,GooUniverse 正在迅速变化。祝你好运
【解决方案2】:

您需要将已弃用的代码替换为:
内容.commit(api, null);

https://developer.android.com/reference/com/google/android/gms/drive/DriveContents.html

【讨论】:

  • 对不起,我已经退休一年了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-11
  • 2018-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多