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

Android Kotlin中Google Drive API的files().list()间歇性返回Null

问题描述

在Android API 34的Kotlin应用中使用Google Drive API,已成功通过API创建名为Inventory_Backup的文件夹,该文件夹能在Google Drive网页端查看,也可正常上传下载文件。但后续调用files().list()接口验证文件夹是否存在时,结果间歇性返回Null,多数情况返回Null,偶尔能得到预期值。


Gradle依赖

implementation 'com.google.android.gms:play-services-auth:19.0.0'
implementation 'com.google.http-client:google-http-client-gson:1.26.0'
implementation('com.google.api-client:google-api-client-android:1.26.0') {
        exclude group: 'org.apache.httpcomponents'
    }
implementation('com.google.apis:google-api-services-drive:v3-rev136-1.25.0') {
        exclude group: 'org.apache.httpcomponents'
    }

Manifest权限配置

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    tools:ignore="ScopedStorage" />

创建Drive服务代码

val googleDriveService =
                Drive.Builder(
                    AndroidHttp.newCompatibleTransport(),
                    GsonFactory(),
                    credential
                )
                    .setApplicationName("House Inventory") 
                    .build()

// DriveServiceHelper封装所有REST API和SAF功能,处理点击事件前必须实例化
mDriveServiceHelper = GoogleDriveServiceHelper(googleDriveService)

GoogleDriveServiceHelper类初始化代码

class GoogleDriveServiceHelper(driveService: Drive) {
private val mDriveService: Drive = driveService

private val FOLDER_MIME_TYPE = "application/vnd.google-apps.folder"
private val FOLDER_NAME = "Inventory_Backup"
private val FILE_MIME_TYPE = "application/zip"
var FILE_NAME = ""

创建文件夹代码

private val FOLDER_MIME_TYPE = "application/vnd.google-apps.folder"
private val FOLDER_NAME = "Inventory_Backup"

fun createFolder(): Task<String> {
    val tcs = TaskCompletionSource<String>()
    val service = Executors.newFixedThreadPool(1)
    val metadata = File()
        .setParents(listOf("root"))
        .setMimeType(FOLDER_MIME_TYPE)
        .setName(FOLDER_NAME)

    service.execute {
        val googleFolder: File?
        try {
            googleFolder = mDriveService.files().create(metadata).execute()
        } catch (e: IOException) {
            e.printStackTrace()
            throw IOException("Null result when requesting Folder creation.")
        }
        val finalResult = googleFolder.id
        Handler(Looper.getMainLooper()).postDelayed({
            tcs.setResult(finalResult)
        }, 1000)
    }

    return tcs.task
    }

验证文件夹存在的代码

val isFolderPresent: Task<String>
    get() = getFolderPresent()

    fun getFolderPresent(): Task<String> {
    val tcs = TaskCompletionSource<String>()
    val service = Executors.newFixedThreadPool(1)

    service.execute {
        var result: FileList? = null
        try {
            result = mDriveService.files().list()
                .setQ("mimeType='$FOLDER_MIME_TYPE' and trashed=false")
                .execute()
            // 此处result间歇性为Null
        } catch (e: IOException) {
            e.printStackTrace()
        }

        var finalResult = ""

        if (result != null)
            for (file in result.files) {
                if (file.name == FOLDER_NAME) finalResult = file.id
            }

        Handler(Looper.getMainLooper()).postDelayed({
            tcs.setResult(finalResult)
        }, 1000)
    }
    return tcs.task
}

解决方案

1. 精准优化查询条件

当前查询未限定父目录为根目录,且未直接过滤文件夹名称,可能搜索到其他目录下的同名文件夹,同时API默认分页返回结果,容易遗漏目标文件夹。修改查询语句:

.setQ("mimeType='$FOLDER_MIME_TYPE' and name='$FOLDER_NAME' and 'root' in parents and trashed=false")

2. 处理分页结果

Google Drive API默认只返回100条结果,需循环获取所有分页数据,避免遗漏:

fun getFolderPresent(): Task<String> {
    val tcs = TaskCompletionSource<String>()
    Executors.newSingleThreadExecutor().execute {
        var finalResult = ""
        var pageToken: String? = null
        try {
            do {
                val result = mDriveService.files().list()
                    .setQ("mimeType='$FOLDER_MIME_TYPE' and name='$FOLDER_NAME' and 'root' in parents and trashed=false")
                    .setPageToken(pageToken)
                    .execute()
                
                result.files?.forEach { file ->
                    finalResult = file.id
                }
                pageToken = result.nextPageToken
            } while (pageToken != null)
        } catch (e: IOException) {
            e.printStackTrace()
        }

        Handler(Looper.getMainLooper()).post {
            tcs.setResult(finalResult)
        }
    }
    return tcs.task
}

3. 移除不必要的延迟

创建文件夹和验证逻辑中的postDelayed(1000)无实际意义,反而可能导致时序问题,直接在任务完成后立即回调:

  • 创建文件夹代码修改为:
Handler(Looper.getMainLooper()).post {
    tcs.setResult(finalResult)
}
  • 验证文件夹代码同理移除延迟。

4. 更新依赖版本

当前使用的Drive API版本较旧,存在兼容性隐患,建议更新至最新稳定版:

implementation 'com.google.android.gms:play-services-auth:20.7.0'
implementation 'com.google.http-client:google-http-client-gson:1.43.3'
implementation('com.google.api-client:google-api-client-android:2.0.1') {
    exclude group: 'org.apache.httpcomponents'
}
implementation('com.google.apis:google-api-services-drive:v3-rev20240512-2.0.1') {
    exclude group: 'org.apache.httpcomponents'
}

5. 检查认证有效性

确保credential未过期,调用API前可检查认证状态:

if (!credential.refreshToken) {
    // 触发重新认证流程
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 21:05:02