You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Google Sign In接入Google Sheets API实现读写操作?

问题:通过Google Sign In接入Google Sheets API(Android应用)

我正在开发一款可访问Google Sheets的Android应用,因需同时支持读写,无法使用仅能读取的API密钥。参考Google Sheets API Java快速入门文档时登录Google账号遇到问题,随后集成Google Sign In功能并编写了MainActivity代码:

package com.example.frcscout22sheets

import Home
import android.content.ContentValues.TAG
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import com.example.frcscout22sheets.databinding.ActivityMainBinding
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInClient
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.SignInButton
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.Scope
import com.google.android.gms.tasks.Task
import com.google.api.services.sheets.v4.SheetsScopes


class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var mGoogleSignInClient: GoogleSignInClient

    companion object {
        val instance = MainActivity()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken("975626648989-jf3cl536froqejdc741ai934ek540ovv.apps.googleusercontent.com")
            .requestScopes(Scope(SheetsScopes.SPREADSHEETS))
            .requestServerAuthCode("975626648989-jf3cl536froqejdc741ai934ek540ovv.apps.googleusercontent.com")
            .requestEmail()
            .build()

        mGoogleSignInClient = GoogleSignIn.getClient(this, gso);

        val dataFragment = Data()
        val allianceSelectionFragment = AllianceSelection()
        val homeFragment = Home()

        val signin = findViewById<SignInButton>(R.id.sign_in_button)

        setCurrentFragment(homeFragment)

        signin.setOnClickListener {
            signIn()
        }

        binding.bottomNavigationView.setOnItemSelectedListener {
            when (it.itemId) {
                R.id.data -> setCurrentFragment(dataFragment)
                R.id.alliance_selection -> setCurrentFragment(allianceSelectionFragment)
                R.id.home -> setCurrentFragment(homeFragment)
            }
            true
        }
    }

    private fun setCurrentFragment(fragment: Fragment) =
        supportFragmentManager.beginTransaction().apply {
            replace(R.id.flFragment, fragment)
            commit()
        }

    private fun signIn() {
        val signInIntent: Intent = mGoogleSignInClient.signInIntent
        startActivityForResult(signInIntent, 1)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        // Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...);
        if (requestCode == 1) {
            // The Task returned from this call is always completed, no need to attach
            // a listener.
            val task: Task<GoogleSignInAccount> = GoogleSignIn.getSignedInAccountFromIntent(data)
            handleSignInResult(task)
        }
    }

    private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
        try {
            val account = completedTask.getResult(ApiException::class.java)

            // Signed in successfully, show authenticated UI.
        } catch (e: ApiException) {
            // The ApiException status code indicates the detailed failure reason.
            // Please refer to the GoogleSignInStatusCodes class reference for more information.
            Log.w(TAG, "signInResult:failed code=" + e.statusCode)
        }
    }
}

完成登录后,不清楚如何利用该登录信息构建Sheets服务以调用API。此前使用快速入门时,通过云控制台下载的.json文件获取凭据构建服务:

private val service = Sheets.Builder(
        HTTPTRANSPORT,
        jsonfactory,
        getCredentials(context, HTTPTRANSPORT)

解决方案

1. 从登录账号中获取Access Token

修改handleSignInResult方法,通过GoogleAuthUtil获取已授权账号的Access Token,这个Token将用于验证Sheets API请求:

private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
    try {
        val account = completedTask.getResult(ApiException::class.java)
        account?.let { googleAccount ->
            // 获取Access Token,指定Sheets的权限范围
            val accessToken = GoogleAuthUtil.getToken(
                this,
                googleAccount.account,
                "oauth2:${SheetsScopes.SPREADSHEETS}"
            )
            // 用Token构建Sheets服务实例
            val sheetsService = buildSheetsService(accessToken)
            // 后续可使用sheetsService调用Sheets API进行读写操作
        }
    } catch (e: ApiException) {
        Log.w(TAG, "signInResult:failed code=" + e.statusCode)
    } catch (e: IOException) {
        Log.e(TAG, "获取Access Token失败", e)
    }
}

2. 构建Sheets服务实例

编写buildSheetsService方法,利用获取到的Access Token作为请求授权凭据,替代之前的.json文件凭据方式:

import com.google.api.client.extensions.android.http.AndroidHttp
import com.google.api.client.json.gson.GsonFactory
import com.google.api.services.sheets.v4.Sheets

private fun buildSheetsService(accessToken: String): Sheets {
    val HTTP_TRANSPORT = AndroidHttp.newCompatibleTransport()
    val JSON_FACTORY = GsonFactory.getDefaultInstance()

    return Sheets.Builder(HTTP_TRANSPORT, JSON_FACTORY)
        .setHttpRequestInitializer { request ->
            // 设置Authorization头,用Bearer Token验证
            request.headers.authorization = "Bearer $accessToken"
        }
        .setApplicationName("你的应用名称") // 替换为你的应用名称
        .build()
}

3. 关键注意事项

  • SHA-1指纹配置:确保在Google Cloud Console的OAuth 2.0客户端ID设置中,已添加你当前Android应用的SHA-1指纹,否则会出现授权失败。
  • 权限范围:当前使用SheetsScopes.SPREADSHEETS支持全量读写操作,若只需读取可改为SheetsScopes.SPREADSHEETS_READONLY。
  • Token刷新:Access Token存在过期时间,可通过GoogleSignInClient.silentSignIn()实现静默刷新,避免用户重复登录。

内容的提问来源于stack exchange,提问作者user17792511

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 04:50:46