引言——AI 助力,移动应用新体验

1.1 传统移动开发的局限与机遇

在今天的移动应用中,我们常常需要处理各种信息。文字、图片、视频、音频……用户希望应用能像人一样“看”懂图片,“听”懂语音。但传统的移动应用在理解这些非结构化数据方面往往力不从心。我们常常需要自己编写复杂的逻辑来识别图像中的物体,或者从大量文字中提取关键信息。这不仅耗时,而且效果有限。

而现在,多模态 AI 的出现为我们带来了新的机遇。它能像人类一样,同时处理和理解不同类型的信息(如图片和文字),让应用变得更加智能和交互。

1.2 为什么选择 Gemini API 和 Jetpack Compose?

  • Gemini API:这是 Google 推出的一系列多模态模型,它能够同时接收图片、文本甚至视频作为输入,并生成智能的回答。想象一下,你的应用不再是简单地展示图片,而是能“看”懂图片,并根据用户的提问给出专业的解释。Gemini API 提供了强大的多模态理解能力,并且在 Android 端有方便集成的 SDK。
  • Jetpack Compose:这是 Android 官方推荐的现代化 UI 工具包。它采用声明式编程范式,让 UI 开发变得更加直观和高效。对于我们这样处理 AI 响应的应用来说,Compose 的响应式特性可以让我们轻松地根据 AI 返回的结果实时更新界面,大大简化了 UI 状态管理。

Gemini API 强大的智能理解能力与 Jetpack Compose 灵活高效的 UI 构建方式相结合,为我们开发新一代智能移动应用提供了完美的平台!

1.3 本文目标:打造一个简单的“智能识图助手”

本文将带你实现一个有趣的 “智能识图助手” 应用。用户可以上传一张图片,然后输入一个问题(比如:“这张图里有什么?”或者“这是什么动物?”),应用会调用 Gemini API 来理解图片和问题,并返回智能的回答。这个应用将让你亲身体验多模态 AI 的魅力,并掌握 Jetpack Compose 与 Gemini API 集成的基本流程。

技术基石——准备好你的开发环境

2.1 Gemini API 核心能力速览

Gemini 模型家族包含多种规模,例如 Gemini 2.5 Pro 适用于处理大量信息和复杂任务,而 Gemini 2.5 Flash 则更轻量高效,适合在设备端或对延迟敏感的场景。对于我们的智能识图应用,我们将主要关注其 多模态输入处理 (Multimodal Input) 能力,即同时接收图片和文字输入。

2.2 Jetpack Compose 基础概念

  • 声明式 UI:你只需要描述 UI 在不同状态下应该是什么样子,而不是手动操作 UI 元素。
  • Composable 函数:构建 UI 的基本单元,用 @Composable 注解标记的函数。
  • 状态管理 (State):Compose 应用中的数据变化,使用 remembermutableStateOf 来管理 UI 状态,并让 UI 响应式地更新。
  • ViewModel:作为 UI 层的状态持有者,帮助我们隔离业务逻辑和网络请求,让 Composable 保持“傻瓜式”,更易于测试和维护。

核心实践——构建你的智能识图助手

接下来实操一波。我们将构建一个界面,让用户可以:

  1. 选择一张图片。
  2. 输入一个问题。
  3. 点击按钮发送图片和问题给 Gemini API。
  4. 显示 Gemini API 返回的回答。

3.1 环境准备与 Gemini API 交互核心逻辑

这是整个应用的核心部分。我们将配置 Gemini API Key,添加必要的依赖,并编写 ViewModel 来处理与 Gemini API 的所有交互逻辑,包括图片和文本的格式转换,以及请求的发送与响应的处理。

3.1.1 配置 Gemini API Key

  1. 为了安全地使用 Gemini API,你的 API Key 不应该直接硬编码在代码中。推荐的做法是将其存储在项目的 local.properties 文件中,并通过 Gradle 构建系统注入到BuildConfig 类中。
    在项目根目录下的 local.properties 文件中添加你的 Gemini API Key。 将 YOUR_KEY_HERE 替换为你在 Google AI Studio 获取到的实际 API Key。
    # local.properties
    GEMINI_API_KEY=YOUR_KEY_HERE  
  1. 修改 build.gradle.kts (Module: app) 以读取 local.properties 中的 Key 并注入到 BuildConfig。 这确保你的应用在编译时能访问到 API Key,同时避免了将其直接暴露在版本控制中。请确保将 localProperties 变量定义在 android 块之前,以保证其在 android 块内部被正确引用。
// app/build.gradle.kts
import java.util.Properties

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.compose)
}

val localProperties = Properties().apply {
    val localPropertiesFile = rootProject.file("local.properties")
    if (localPropertiesFile.exists()) {
        localPropertiesFile.inputStream().use { load(it) }
    }
}

android {
    namespace = "com.tovl.smartimageassistant"
    compileSdk = 36

    defaultConfig {
        applicationId = "com.tovl.smartimageassistant"
        minSdk = 24
        targetSdk = 36
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables.useSupportLibrary = true

    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }

	// 【在 android {} 块内部, buildTypes {} 块之后,统一为所有构建类型设置 GEMINI_API_KEY】
    val geminiApiKey = localProperties.getProperty("GEMINI_API_KEY")
    if (geminiApiKey != null && geminiApiKey.isNotBlank()) {
        buildTypes.all {
            buildConfigField("String", "GEMINI_API_KEY", "\"$geminiApiKey\"")
        }
    } else {
        buildTypes.all {
            buildConfigField("String", "GEMINI_API_KEY", "\"\"")
        }
        // 警告信息...
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions {
        jvmTarget = "11"
    }
    buildFeatures {
        compose = true
        buildConfig = true // 【确保此行存在,用于生成 BuildConfig 类】
    }
}

dependencies {

    implementation(platform(libs.androidx.compose.bom))

    // ...

    // Jetpack Compose 基础
    implementation(platform("androidx.compose:compose-bom:2025.12.00"))

    implementation("androidx.compose.material:material-icons-core")
    implementation("androidx.compose.material:material-icons-extended")

    // Gemini API 客户端库
    implementation("com.google.ai.client.generativeai:generativeai:0.9.0")

    // Coil for image loading (用于加载选中的图片)
    implementation("io.coil-kt:coil-compose:2.7.0")

    // ViewModel 和 LiveData/StateFlow 整合
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.10.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.10.0")
    implementation("androidx.activity:activity-compose:1.12.1")

}

3.1.2 添加必要的 Android 权限

为了让应用能够访问网络(与 Gemini API 通信)和读取设备存储中的图片,我们需要在 AndroidManifest.xml 中声明相应的权限。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.tovl.smartimageassistant">

    <!-- 访问网络的权限,用于调用 Gemini API -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 从相册选择图片所需的权限,maxSdkVersion 32 适用于旧版本Android -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
    <!-- Android 13 (API 33) 及以上读取媒体图片所需的权限 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SmartImageAssistant">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.SmartImageAssistant">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

3.1.3 构建 SmartImageViewModel 处理业务逻辑

SmartImageViewModel 将负责管理应用的状态(如选定的图片、用户输入的问题、Gemini API 的响应、加载状态),并封装与 Gemini API 交互的逻辑。

// SmartImageViewModel.kt
import com.google.ai.client.generativeai.type.content // 导入 content 构建器
// ...
import com.tovl.smartimageassistant.BuildConfig // 导入 BuildConfig 以访问 API Key

class SmartImageViewModel : ViewModel() {

    // 保存用户选择的图片 Uri
    private val _selectedImageUri = MutableStateFlow<Uri?>(null)
    val selectedImageUri: StateFlow<Uri?> = _selectedImageUri.asStateFlow()

    // 保存用户输入的问题文本
    private val _questionText = MutableStateFlow("")
    val questionText: StateFlow<String> = _questionText.asStateFlow()

    // 保存 Gemini API 的响应文本
    private val _responseText = MutableStateFlow("等待你的提问...")
    val responseText: StateFlow<String> = _responseText.asStateFlow()

    // 表示当前是否正在加载/等待 API 响应
    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    // Gemini AI 模型实例,现在是可空的,因为初始化可能失败
    private var generativeModel: GenerativeModel? = null

    init {
        // 在 ViewModel 初始化时尝试创建 GenerativeModel 实例
        val apiKey = BuildConfig.GEMINI_API_KEY // 访问 BuildConfig 中的 API Key
        if (apiKey.isBlank()) {
            _responseText.value = "错误:Gemini API Key 未配置!请检查 local.properties。"
            // 如果 API Key 缺失,generativeModel 保持为 null
        } else {
            generativeModel = GenerativeModel(
                modelName = "gemini-2.5-flash", // 【模型选择】使用支持视觉输入的多模态模型。这里我选择 gemini-2.5-flash.
                apiKey = apiKey
            )
        }
    }

    /**
     * 更新选中的图片 Uri。
     * @param uri 新的图片 Uri,如果为 null 则表示没有选择图片。
     */
    fun setSelectedImageUri(uri: Uri?) {
        _selectedImageUri.value = uri
        // 根据是否有图片选择,更新响应文本提示
        _responseText.value = if (uri != null) "图片已选择,请输入问题。" else "请选择图片并输入问题!"
    }

    /**
     * 更新用户输入的问题文本。
     * @param text 用户输入的问题字符串。
     */
    fun setQuestionText(text: String) {
        _questionText.value = text
    }

    /**
     * 发送图片和问题给 Gemini API。
     * @param context 当前应用的 Context,用于 Uri 到 Bitmap 的转换。
     */
    fun sendQuery(context: Context) {
        // 确保 generativeModel 已初始化
        val currentModel = generativeModel
        if (currentModel == null) {
            _responseText.value = "错误:Gemini API Key 未配置,无法发送请求。"
            _isLoading.value = false // 确保即使出错也要停止加载状态
            return
        }

        // 确保有图片和问题才能发送请求
        if (_selectedImageUri.value == null || _questionText.value.isBlank()) {
            _responseText.value = "请选择图片并输入问题!"
            return
        }

        viewModelScope.launch { // 在 ViewModel 的作用域内启动协程
            _isLoading.value = true // 设置加载状态为 true
            _responseText.value = "思考中..." // 显示加载状态提示

            try {
                // 1. 将 Uri 转换为 Bitmap
                val bitmap = uriToBitmap(context, _selectedImageUri.value!!)
                    ?: throw IllegalStateException("无法加载图片,请尝试其他图片。")

                // 2. 构建 Gemini API 所需的 Content 对象
                // Content 可以同时包含多个 Part,这里我们包含一个图片和一个文本
                val content = content {
                    image(bitmap) // 直接传递 Bitmap 对象给 image 函数
                    text(_questionText.value) // 添加文本部分
                }

                // 3. 调用 Gemini API
                val response = currentModel.generateContent(content) // 使用已检查的 currentModel
                _responseText.value = response.text ?: "未获取到回答,请稍后再试或换个问题。"

            } catch (e: Exception) {
                // 处理可能发生的错误,如网络问题、API 错误等
                _responseText.value = "请求失败: ${e.message}"
                e.printStackTrace() // 打印错误栈,方便调试
            } finally {
                _isLoading.value = false // 无论成功失败,都结束加载状态
            }
        }
    }

    /**
     * 辅助函数:将 Uri 转换为 Bitmap。
     * 兼容不同 Android 版本获取图片。
     * @param context 应用上下文。
     * @param uri 图片的 Uri。
     * @return 转换后的 Bitmap 对象,如果失败则返回 null。
     */
    private fun uriToBitmap(context: Context, uri: Uri): Bitmap? {
        return try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { // Android P (API 28) 及以上
                val source = ImageDecoder.createSource(context.contentResolver, uri)
                ImageDecoder.decodeBitmap(source)
            } else { // Android P 以下
                @Suppress("DEPRECATION") // 抑制对旧API的警告
                MediaStore.Images.Media.getBitmap(context.contentResolver, uri)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }
}

温馨提示

  • API Key:你的 Gemini API Key 应该妥善保管,不要直接硬编码在代码中。通常做法是放在 local.properties 文件中,并通过 build.gradle.kts 配置到 BuildConfig 中。
    怎么获取?使用你的谷歌帐号,在 -> https://aistudio.google.com/ 获取。
  • 模型选择:对于图片理解,请确保选择支持视觉输入的 Gemini 模型,例如 gemini-2.5-flash-imagegemini-2.5-flash
  • 权限:别忘了在 AndroidManifest.xml 中添加读取外部存储的权限(如果你的目标 SDK 版本需要),以及网络权限。

3.2 应用 UI 布局

应用的主界面将采用 Jetpack Compose 构建,提供简洁直观的用户体验。MainActivity 作为应用的入口点,会设置 Compose 的内容视图,并管理 SmartImageAppScreen Composable 的生命周期。

// MainActivity.kt
// ...
// 【确保 Icons 和 Icons.Default.AddAPhoto 的导入】
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AddAPhoto
import androidx.compose.material3.* // 导入所有 Material3 相关 Composable
import androidx.compose.runtime.* // 导入所有 Compose Runtime 相关,包括 @Composable, remember, collectAsState 等
// ...
// 【确保 LocalContext 的导入】
import androidx.compose.ui.platform.LocalContext 

import androidx.lifecycle.viewmodel.compose.viewModel // 导入 viewModel() 函数

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            SmartImageAssistantTheme {
                // Scaffold 提供了基本的布局结构,如顶部栏、抽屉、浮动按钮等
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    // 将 ViewModel 注入到 Compose 层次结构中
                    val viewModel: SmartImageViewModel = viewModel()
                    SmartImageAppScreen(
                        viewModel = viewModel,
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }
}

/**
 * 智能识图助手应用的主 Composable 屏幕。
 * @param viewModel 负责处理业务逻辑和状态的 ViewModel。
 * @param modifier 适用于此 Composable 的 Modifier。
 */
@OptIn(ExperimentalMaterial3Api::class) // 标记为实验性 API,因为 TopAppBar 是 M3 的新组件
@Composable
fun SmartImageAppScreen(
    viewModel: SmartImageViewModel,
    modifier: Modifier = Modifier
) {
    // 从 ViewModel 收集状态,当状态变化时会触发 UI 重组
    val selectedImageUri by viewModel.selectedImageUri.collectAsState()
    val questionText by viewModel.questionText.collectAsState()
    val responseText by viewModel.responseText.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()

    // 用于启动图片选择器的 Activity Result Launcher
    val pickImageLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent() // 用于获取图片内容
    ) { uri: Uri? ->
        viewModel.setSelectedImageUri(uri) // 将选中的 Uri 传给 ViewModel
    }

    val context = LocalContext.current

    // 使用 Column 垂直布局所有元素,并添加滚动能力
    Column(
        modifier = modifier
            .fillMaxSize()
            .padding(16.dp)
            .verticalScroll(rememberScrollState()), // 使内容可滚动,防止内容溢出
        horizontalAlignment = Alignment.CenterHorizontally // 水平居中所有子元素
    ) {
        // 顶部应用栏
        TopAppBar(
            title = { Text("智能识图助手") },
            colors = TopAppBarDefaults.topAppBarColors(
                containerColor = MaterialTheme.colorScheme.primaryContainer,
                titleContentColor = MaterialTheme.colorScheme.primary,
            ),
            modifier = Modifier.fillMaxWidth()
        )
        Spacer(modifier = Modifier.height(16.dp))

        // 1. 图片选择和预览区域
        Card(
            modifier = Modifier
                .fillMaxWidth()
                .height(200.dp)
                // 仅在非加载状态下允许点击选择图片
                .clickable(enabled = !isLoading) { pickImageLauncher.launch("image/*") } 
                .clip(RoundedCornerShape(8.dp)), // 设置圆角
            shape = RoundedCornerShape(8.dp) // Card 自身也需要设置圆角
        ) {
            Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center // Box 内内容居中
            ) {
                if (selectedImageUri != null) {
                    // 使用 Coil 库异步加载图片并显示
                    Image(
                        painter = rememberAsyncImagePainter(model = selectedImageUri),
                        contentDescription = "Selected Image",
                        modifier = Modifier.fillMaxSize(),
                        contentScale = ContentScale.Crop // 裁剪以填充空间,保持图片比例
                    )
                } else {
                    Text("点击选择图片", style = MaterialTheme.typography.titleMedium)
                }
                // 蒙层和图标提示,增加交互感
                if (!isLoading) {
                    Icon(
                        imageVector = Icons.Default.AddAPhoto, // 添加图片图标
                        contentDescription = "Select Image",
                        tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f), // 半透明效果
                        modifier = Modifier.size(48.dp)
                    )
                }
            }
        }
        Spacer(modifier = Modifier.height(16.dp))

        // 2. 问题输入框
        OutlinedTextField(
            value = questionText,
            onValueChange = { viewModel.setQuestionText(it) }, // 更新 ViewModel 中的问题文本
            label = { Text("请输入你的问题...") },
            modifier = Modifier.fillMaxWidth(),
            singleLine = false, // 允许多行输入
            maxLines = 3,
            enabled = !isLoading // 加载时禁用输入框
        )
        Spacer(modifier = Modifier.height(16.dp))

        // 3. 发送按钮
        Button(
            onClick = { viewModel.sendQuery(context) }, // 传入当前 Context 给 ViewModel
            modifier = Modifier.fillMaxWidth(),
            // 仅在非加载、已选择图片且问题不为空时启用按钮
            enabled = !isLoading && selectedImageUri != null && questionText.isNotBlank() 
        ) {
            if (isLoading) {
                CircularProgressIndicator(
                    modifier = Modifier.size(24.dp),
                    color = MaterialTheme.colorScheme.onPrimary // 进度条颜色
                )
                Spacer(modifier = Modifier.width(8.dp))
                Text("发送中...")
            } else {
                Text("发送给 Gemini")
            }
        }
        Spacer(modifier = Modifier.height(16.dp))

        // 4. AI 响应显示区域
        Text(
            text = "Gemini 的回答:\n$responseText",
            modifier = Modifier.fillMaxWidth(),
            style = MaterialTheme.typography.bodyLarge,
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Start // 左对齐
        )
    }
}

3.3 图片选择与预览

应用的用户界面设计围绕着直观的图片选择和实时预览展开。这部分通过 ActivityResultContracts.GetContent() 启动系统图库选择图片,并利用 Coil 库高效地加载和显示所选图片。

3.3.1 图片选择器集成

通过 rememberLauncherForActivityResultActivityResultContracts.GetContent(),我们创建了一个用于启动图片选择器的 Activity Result Launcher。当用户点击图片预览区域时,会触发图片选择器。

    // ...
    val selectedImageUri by viewModel.selectedImageUri.collectAsState()
    // ...

    // 用于启动图片选择器的 Activity Result Launcher
    val pickImageLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent() // 用于获取图片内容 (如 "image/*")
    ) { uri: Uri? ->
        viewModel.setSelectedImageUri(uri) // 将选中的 Uri 传给 ViewModel
    }
    // ...

        // 1. 图片选择和预览区域
        Card(
            modifier = Modifier
                .fillMaxWidth()
                .height(200.dp)
                // 仅在非加载状态下允许点击选择图片
                .clickable(enabled = !isLoading) { pickImageLauncher.launch("image/*") } 
                .clip(RoundedCornerShape(8.dp)), // 设置圆角
            shape = RoundedCornerShape(8.dp) // Card 自身也需要设置圆角
        ) {
            // ... (预览逻辑) ...
        }
    // ...

3.3.2 图片预览显示

选中的图片会立即显示在应用界面上的 Card 区域。我们使用 Coil 库(一个流行的 Android 图片加载库)来异步加载和展示图片,确保 UI 的流畅性。同时,当没有图片被选中时,会显示一个提示文本和图标。

    // ...
        // 1. 图片选择和预览区域
        Card(
            modifier = Modifier
                .fillMaxWidth()
                .height(200.dp)
                .clickable(enabled = !isLoading) { pickImageLauncher.launch("image/*") }
                .clip(RoundedCornerShape(8.dp)),
            shape = RoundedCornerShape(8.dp)
        ) {
            Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) {
                if (selectedImageUri != null) {
                    // 使用 Coil 库异步加载图片并显示
                    Image(
                        painter = rememberAsyncImagePainter(model = selectedImageUri),
                        contentDescription = "Selected Image",
                        modifier = Modifier.fillMaxSize(),
                        contentScale = ContentScale.Crop // 裁剪以填充空间,保持图片比例
                    )
                } else {
                    Text("点击选择图片", style = MaterialTheme.typography.titleMedium)
                }
                // 蒙层和图标提示,增加交互感
                if (!isLoading) {
                    Icon(
                        imageVector = Icons.Default.AddAPhoto, // 添加图片图标
                        contentDescription = "Select Image",
                        tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f), // 半透明效果
                        modifier = Modifier.size(48.dp)
                    )
                }
            }
        }
    // ...

3.4 效果展示

点击智能视图助手中间的相机按钮,上传本地图片。然后询问 Gemini AI 一段话。点击发送。
使用效果如图:

  • 小猫图片识别

在这里插入图片描述

  • 花图识别

在这里插入图片描述

(可以看出,gemini-2.5-flash 这个模型的图片识别能力还是不错的~很多特征描述的很准确)
当然,界面可能稍显简陋,不过可改进的方向也很明确,比如流式输出以避免UI阻塞Markdown格式渲染甚至Latex公式的渲染支持等等。

总结与展望

4.1 总结

通过这个简单的“智能识图助手”应用,我们成功地将 Gemini API 的多模态理解能力与 Jetpack Compose 现代化、响应式的 UI 框架结合起来。你已经学会了:

  • 如何在 Android 项目中集成 Gemini API。
  • 如何构建支持图片选择和文本输入的 Compose UI。
  • 如何将图片转换为 Gemini API 可识别的格式。
  • 如何发送多模态请求并处理 AI 的响应。

这只是多模态 AI 应用的冰山一角,但它为你打开了通往更智能、更具交互性的移动应用世界的大门!

4.2 展望未来:更多可能

  • Function Calling(函数调用):这是一个非常强大的功能!它允许 Gemini 模型在理解用户意图后,调用你应用中的本地函数。比如,如果用户问“这张照片里的花叫什么名字,附近哪里有花店?”,Gemini 在识别出花的同时,可以触发一个本地的 findNearbyFlowerShops(flowerName: String) 函数,然后将函数返回的结果(比如花店列表)再总结成自然的语言回复给用户。这会极大地扩展 AI 应用的交互能力。你可以将其作为下一个进阶学习的目标。
  • 实时视频流分析:将图片输入扩展到实时视频流,实现实时物体识别、行为分析等。
  • 音频输入与理解:除了文本和图片,还可以集成语音识别,让用户通过语音提问。
  • 更丰富的 UI 展现:例如,在识别出的物体上叠加边框或标签,甚至使用动画(如 Lottie 动画)来模拟 AI 的“思考”过程,让用户体验更生动。
  • 错误处理与用户引导:完善错误提示和加载动画,提供更好的用户引导。

仓库链接……

我将本文项目代码上传至了GitHub,欢迎下载学习!⭐️
SmartImageAssistant

作者:徐宇强
原文链接:初探多模态AI:用 Jetpack Compose 和 Gemini API 打造智能识图应用

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐