Android 端实现谷歌备份与恢复

Google 云端控制台

  1. 访问 Google Cloud Console

  2. 如果还没有项目,请创建一个。如果已有项目,请选择该项目。

  3. 在导航菜单中选择 APIs & Services > Dashboard

  4. 点击 ENABLE APIS AND SERVICES 按钮。搜索 "Google Drive API",然后点击 Google Drive API 进入详情页面。点击 Enable 按钮启用此 API。

    1732003634450.jpg

  5. 返回到导航栏,选择 APIs & Services > Credentials

  6. 点击 CREATE CREDENTIALS 按钮,并从下拉列表中选择 OAuth client ID

  7. 在 "Application type" 选项中,选择 "Android":

    • 输入 Name
    • Signing-certificate fingerprint 中输入您的应用程序签名证书的 SHA-1 指纹。如果您不知道如何获取 SHA-1 指纹,请参考这篇文章
    • Package name 中输入您的 Android 应用程序的包名。包名必须与您实际应用的包名一致,否则授权将无法成功。
  8. 点击 Create。现在,您将看到您的 Client ID。请务必安全地保存这些信息,稍后将在您的应用程序中使用它们。

Android端依赖

    implementation 'com.google.android.gms:play-services-auth:19.2.0'
    implementation 'com.google.apis:google-api-services-drive:v3-rev197-1.25.0'
    implementation 'com.google.api-client:google-api-client-android:1.23.0'

Android端代码

1.初始化服务,

    private lateinit var googleSignInClient: GoogleSignInClient
    private var isRecover: Boolean=false
    private val RC_SIGN_IN = 9001
    private fun initDrive() {
        val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestEmail()
            .requestScopes(Scope(DriveScopes.DRIVE_FILE))
            .build()
        googleSignInClient = GoogleSignIn.getClient(this, signInOptions)
    }

切记 使用 requestScopes(Scope(DriveScopes.DRIVE_FILE)) 而不是 .requestIdToken(ApiAction.SERVER_CLIENT_ID)
.requestIdToken(ApiAction.SERVER_CLIENT_ID)是登录注册使用的

2.具体业务逻辑

点击事件

  views.backupGoogle.debouncedClicks {
            if (isGooglePlayServicesAvailable(this)) {
                isRecover = false
                getDriveAuth()
            } else {
                T.showShort(this, "设备不支持谷歌登录")
            }
        }
        views.recoverGoogle.debouncedClicks {
            if (isGooglePlayServicesAvailable(this)) {
                isRecover = true
                getDriveAuth()
            } else {
                T.showShort(this, "设备不支持谷歌登录")
            }
        }

详细方法

  fun isGooglePlayServicesAvailable(context: Context): Boolean {
        val googleApiAvailability = GoogleApiAvailability.getInstance()
        val resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context)
        return resultCode == ConnectionResult.SUCCESS
    }

    private fun getDriveAuth() {
        val signInIntent = googleSignInClient.signInIntent
        startActivityForResult(signInIntent, RC_SIGN_IN)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == RC_SIGN_IN) {
            val task = GoogleSignIn.getSignedInAccountFromIntent(data)
            handleSignInResult(task)
        }
    }

    private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
        try {
            val account = completedTask.getResult(ApiException::class.java)
            account?.let {
                // Get a Drive service instance using the signed-in account.
                getDriveService(this, it)?.let {drive->
                    lifecycleScope.launch(Dispatchers.IO) {
                        if(isRecover){
                            BackupUtil.restoreData(drive, "backup.txt").let { content->
                                L.v("===content==${content}")
                            }
                        }else{
                            BackupUtil.backupData(drive, privateHex, "backup.txt")
                        }
                    }
                }
            }
        } catch (e: ApiException) {
            L.v("=====signInResult:failed code=" + e.statusCode)
        }
    }

BackupUtil代码,里面有覆盖上传以及更新上传,自我斟酌

import android.content.Context
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.api.client.extensions.android.http.AndroidHttp
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential
import com.google.api.client.http.InputStreamContent
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.drive.Drive
import com.google.api.services.drive.DriveScopes
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.nio.charset.StandardCharsets

/**
 * Author : lisir
 * Time: 2024/11/18 14:17
 * Describe :
 */
object BackupUtil {
    /**
     *实例化Google Drive
     */
    fun getDriveService(context: Context, account: GoogleSignInAccount): Drive? {
        return try {
            val credential = GoogleAccountCredential.usingOAuth2(
                context, listOf(DriveScopes.DRIVE_FILE)
            )
            credential.selectedAccount = account.account
            Drive.Builder(
                AndroidHttp.newCompatibleTransport(),
                JacksonFactory.getDefaultInstance(),
                credential
            ).setApplicationName(context.getString(R.string.app_name))
                .build()
        } catch (e: Exception) {
            L.v("====" + e.message)
            null
        }
    }


    /**
     * @param drive :Google Drive
     * @param data :写入数据 eg:真是美好的一天
     * @param fileName :文件夹名称 eg: back.txt
     * 这个方法可以实现云盘上只有一份文件
     */
    fun backupData(drive: Drive, data: String, fileName: String):String? {
        try {
            val query = "mimeType='text/plain' and trashed=false and name='$fileName'"
            val existingFiles = drive.files().list()
                .setQ(query)
                .setSpaces("drive")
                .setFields("nextPageToken, files(id, name)")
                .setPageSize(1)
                .execute()

            val metadata = com.google.api.services.drive.model.File()
                .setName(fileName)
                .setMimeType("text/plain")

            val contentStream = ByteArrayInputStream(data.toByteArray(StandardCharsets.UTF_8))
            val content = InputStreamContent("text/plain", contentStream)

            if (existingFiles.files.isEmpty()) {
                val newFile = drive.files().create(metadata, content).setFields("id").execute()
                L.v("File ID=====:"+newFile.id)
                return  newFile.id
            } else {
                val fileId = existingFiles.files[0].id
                val updatedFile = drive.files().update(fileId, metadata, content).setFields("id").execute()
                L.v("Updated File ID:===="+updatedFile.id)
                return  updatedFile.id
            }
        }catch (e: Exception) {
            L.v("====" + e.message)
            return  null
        }

    }


    /**
     * 当云盘只有一个名字唯一的文件时候,
     * @param drive :Google Drive
     * @param fileName :文件夹名称 eg: back.txt
     */
    fun restoreData(drive: Drive, fileName: String): String? {
        try {
            val query = "name='$fileName'"
            val fileList = drive.files().list()
                .setQ(query)
                .execute()
            if (fileList.files.isEmpty()) {
                return null
            }
            val backupFile = fileList.files.first()
            val outputStream = ByteArrayOutputStream()
            drive.files().get(backupFile.id).executeMediaAndDownloadTo(outputStream)
            return String(outputStream.toByteArray(), StandardCharsets.UTF_8)
        } catch (e: Exception) {
            L.v("====" + e.message)
            return  null
        }
    }

    
    /**
     * 当云盘有多个名字相同的文件时候,
     * @param drive :Google Drive
     * @param fileName :文件夹名称 eg: back.txt
     */
    fun restoreDataMore(drive: Drive, fileName: String): String? {
        val query = "mimeType='text/plain' and trashed=false and name='$fileName'"
        val existingFiles = drive.files().list()
            .setQ(query)
            .setSpaces("drive")
            .setFields("nextPageToken, files(id, name)")
            .setPageSize(1)
            .execute()
        if (existingFiles.files.isEmpty()) {
            L.v("File not found:=="+fileName)
            return null
        }
        // Download the file's content
        val fileId = existingFiles.files[0].id
        L.v("fileId:=="+fileId)
        ByteArrayOutputStream().use { outputStream ->
            drive.files().get(fileId)
                .executeMediaAndDownloadTo(outputStream)

            // Convert the downloaded content to a string
            return outputStream.toString(StandardCharsets.UTF_8.name())
        }
    }

    /**
     * @param drive :Google Drive
     * @param data :写入数据 eg:真是美好的一天
     * @param fileName :文件夹名称 eg: back.txt
     * 这个方法可以实现云盘上有多份相同文件
     */
    fun backupDataMore(drive: Drive, data: String, fileName: String) {
        val metadata = com.google.api.services.drive.model.File()
            .setName(fileName)
            .setMimeType("text/plain")

        val contentStream = ByteArrayInputStream(data.toByteArray(StandardCharsets.UTF_8))
        val content = InputStreamContent("text/plain", contentStream)
        val file = drive.files().create(metadata, content).setFields("id").execute()
        L.v("File ID: " + file.id)
    }
    
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容