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

使用Google Sheets API的Kivy/Buildozer安卓应用启动崩溃求助

问题描述

我在小米Redmi Note 12 Pro上运行用Buildozer打包的Kivy应用,该应用需要调用Google Sheets API实现按钮点击写入数据到谷歌表格的功能。应用在本地调试正常,但打包部署到手机后启动就崩溃,移除Google Sheets相关功能则无此问题。

相关代码片段如下:

import os
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

# 初始化服务的函数
def GetService(api_name, api_version, *scopes):
    SCOPES = [scope for scope in scopes[0]]
    creds = None
    if os.path.exists("token.json"):
        creds = Credentials.from_authorized_user_file("token.json", SCOPES)
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                "credentials.json", SCOPES
            )
            creds = flow.run_local_server(port = 0)
        with open("token.json", "w") as token:
            token.write(creds.to_json())
    
    try:
        service = build(api_name, api_version, credentials = creds)
        print("Successfully connected to the google sheets api.")
        return service
    except Exception as e:
        print("Could not connect to the google sheets api!") 
        print(e)
        return None

# 应用启动时初始化的内容
self.api_name = "sheets"
self.api_version = "v4"
self.scopes = ["https://www.googleapis.com/auth/spreadsheets"]
self.sheet_id = "id of my spreadsheet"
self.sheet_name = "name of my sheet"
self.service = GetService(self.api_name, self.api_version, self.scopes)
self.my_sheet = self.service.spreadsheets().get(spreadsheetId = self.sheet_id).execute()

# 按钮点击时添加行的函数
def AddRow(self, current_date, val1, val2, val3, current_time):
        # 获取现有行数
        row_numb = self.service.spreadsheets().values().get(
            spreadsheetId = self.sheet_id,
            majorDimension = "ROWS",
            range = f"{self.sheet_name}A1:A1000"
        ).execute()
        row_numb = len(row_numb["values"]) + 1
        # 准备插入的新行数据
        insert_vals = (
            (current_date, val1, val2, val3, "", current_time, ""), ()
        )
        value_range_body = {
            "majorDimension": "ROWS",
            "values": insert_vals
        }
        self.service.spreadsheets().values().update(
            spreadsheetId = self.sheet_id, 
            valueInputOption = "USER_ENTERED",
            range = self.sheet_name + f"A{row_numb}",
            body = value_range_body
        ).execute()
        return None

应用使用了credentials.json和token.json文件,已将其添加到源文件中,也尝试调整过buildozer.spec的依赖和权限,但问题仍未解决。请问是否仅通过修改.spec文件即可修复?若可以,在默认文件上需做哪些最小变更才能让应用正常运行?


解决方案

仅修改buildozer.spec文件无法完全解决问题,因为代码中存在安卓环境不兼容的逻辑,但.spec文件的调整是运行基础,以下是必要的最小变更:

一、buildozer.spec的核心修改

  1. 添加网络权限
    在android.permissions字段中补充联网权限,谷歌API需要网络请求:
android.permissions = INTERNET
  1. 补充依赖库
    在requirements字段中加入谷歌API相关依赖,确保打包时包含这些库:
requirements = python3,kivy,google-auth,google-auth-oauthlib,google-api-python-client,requests
  1. 确保JSON文件被打包
    确认source.include_exts包含.json后缀,保证credentials.json和token.json被打包进APK:
source.include_exts = py,png,jpg,kv,atlas,json
  1. 指定兼容的安卓API级别
    设置为28及以上的API级别,避免兼容性问题:
android.api = 33

二、代码层面必须调整的逻辑

崩溃的核心原因是代码中flow.run_local_server()是为桌面环境设计的,安卓无法启动本地服务器完成OAuth授权。需要调整为安卓兼容的授权方式:

  • 预先在桌面环境生成有效的token.json(确保包含有效refresh_token),打包时直接包含该文件;
  • 移除桌面授权流程,仅保留通过refresh_token自动刷新token的逻辑。

调整后的GetService函数示例:

def GetService(api_name, api_version, *scopes):
    SCOPES = [scope for scope in scopes[0]]
    creds = None
    # 安卓环境下直接读取打包好的token.json
    if os.path.exists("token.json"):
        creds = Credentials.from_authorized_user_file("token.json", SCOPES)
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            # 仅通过refresh_token刷新,跳过桌面授权流程
            creds.refresh(Request())
            with open("token.json", "w") as token:
                token.write(creds.to_json())
        else:
            # 安卓环境无法完成授权,直接返回None或提示用户在桌面生成token
            print("无法在安卓上完成授权,请先在桌面生成有效token.json")
            return None
    
    try:
        service = build(api_name, api_version, credentials=creds)
        print("Successfully connected to the google sheets api.")
        return service
    except Exception as e:
        print("Could not connect to the google sheets api!") 
        print(e)
        return None

另外注意:安卓应用的工作目录是私有内部存储,确保JSON文件被放置在正确路径,或通过user_data_dir获取应用私有存储路径来读取文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 01:00:13