前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >安卓软件开发,JetpakComposeM3用Gemini API创建demo的技术实现和源码

安卓软件开发,JetpakComposeM3用Gemini API创建demo的技术实现和源码

原创
作者头像
Nimyears
发布2024-12-05 12:19:06
发布2024-12-05 12:19:06
1290
举报
文章被收录于专栏:JetpackCompose M3玩转全栈

2024年进入尾声,作为一名聋人独立开发者,我常常回顾和反思过去半年的成长和收获。最近,我在Gemma2竞赛独立开发项目中再创佳绩,荣获亚军,然后我要专注工作。我会抽时间编写有价值的技术文章,感谢大家的理解与支持!

一、项目背景

Gemini API 是 Google 提供的一款强大的 AI 解决方案,它为移动应用开发者提供了丰富的 AI 功能,比如自然语言处理、图片识别等。用Gemini API,可以轻松把智能化功能集成到你的Android应用中。特别是对那些想要快速嵌入AI功能的开发者来说,这个API提供了很大的便利。

为什么要使用Gemini API?

因为随着移动设备的普及,越来越多的应用需要利用云端的AI服务来减轻本地设备的压力。通过Gemini API,应用可以在云端执行复杂的AI任务,比如处理大规模的图像识别,自然语言生成,确保本地应用的性能不会受到影响。

在这篇文章中,我们会带你一步步地从零开始,用Android Studio新建一个基于Gemini API的项目。作为聋人独立开发者,你不需要太多繁杂的配置,通过提供的模板就能轻松上手。

二、项目开发

要开始一个新的Gemini项目,首先,你需要打开Android Studio,选择“新建项目”选项。在弹出的界面中,找到“Gemini API Starter”模板,这个模板是Google专门为集成Gemini API准备的(参见图示)。

点击"Next",你需要填写以下项目信息:

  • Name: 项目名称,可以自定义,例如“NimGemini”。
  • Package Name: 包名,保持唯一性,可以选择如 com.nim.nimgeminidemo
  • Save Location: 项目保存路径,可以是本地的任意文件夹。
  • Minimum SDK: 选择API 34 (Android 14.0)。
  • Build configuration language: 选择 Kotlin DSL。

接下来,你会被要求输入一个API Key。这个API Key是为了访问Gemini API服务必须的。点击页面上的"Generate API key with Google AI Studio",进入Google AI Studio获取API Key并填入框中。确保API Key准确无误,最后点击“Finish”完成项目创建。

Keys官方链接:https://aistudio.google.com/app/apikey?hl=zh-cn

创建完项目后,Android Studio会生成一个完整的项目结构,其中包括:

  • MainActivity.kt: 这个文件是项目的入口,也是你开始调用Gemini API的地方。
  • build.gradle.kts: 这个文件管理项目的依赖和构建配置。

Gemini API所需的依赖已经通过模板自动添加,无需手动引入。这样,你的项目已经具备了AI调用的能力。

2.3 添加依赖与权限

为了让你的应用成功连接到Gemini API服务,还需要在AndroidManifest.xml中添加一些权限。特别是与网络相关的权限,包括:

代码语言:javascript
复制
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

这两项权限允许应用访问网络服务,并能与Google的AI服务器通信。

2.4 配置API密钥与环境

API Key 是项目中至关重要的一部分。在项目配置文件 gradle.properties 中,你可以将API Key保存为环境变量,这样不仅可以提高安全性,还能避免在代码中直接暴露Key:

代码语言:javascript
复制
API_KEY="你的API密钥"

在代码中,通过读取环境变量的方式获取API Key,这样既便捷又安全。

通过使用 Gemini API Starter 模板,所有的依赖都已经配置好,只需关注核心代码的实现。

2.5 讲解核心代码

(1).我详细介绍Demo中的核心代码部分,以下代码展示了如何通过 Gemini API 发送图片和提示信息,生成AI输出内容:

代码语言:java
复制
package com.nim.nimgeminidemo

class BakingViewModel : ViewModel() {
    private val _uiState: MutableStateFlow<UiState> =
        MutableStateFlow(UiState.Initial)
    val uiState: StateFlow<UiState> =
        _uiState.asStateFlow()

    private val generativeModel = GenerativeModel(
        modelName = "gemini-pro-vision",
        apiKey = BuildConfig.apiKey
    )

    fun sendPrompt(
        bitmap: Bitmap,
        prompt: String
    ) {
        _uiState.value = UiState.Loading

        viewModelScope.launch(Dispatchers.IO) {
            try {
                val response = generativeModel.generateContent(
                    content {
                        image(bitmap)
                        text(prompt)
                    }
                )
                response.text?.let { outputContent ->
                    _uiState.value = UiState.Success(outputContent)
                }
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.localizedMessage ?: "")
            }
        }
    }
}

(2).解释代码

ViewModel 通过 sendPrompt 函数,图片和提示信息发送至 Gemini API 并获取AI生成的文本结果。在界面上,会根据返回的状态更新UI,展示生成的文本或错误信息。

代码语言:java
复制
package com.nim.nimgeminidemo

/**
 * A sealed hierarchy describing the state of the text generation.
 */
sealed interface UiState {

    /**
     * Empty state when the screen is first shown
     */
    object Initial : UiState

    /**
     * Still loading
     */
    object Loading : UiState

    /**
     * Text has been generated
     */
    data class Success(val outputText: String) : UiState

    /**
     * There was an error generating text
     */
    data class Error(val errorMessage: String) : UiState
}

(3)解释代码:

  1. Initial(初始状态):当界面刚加载时,什么都还没开始做,这个状态就代表一切刚刚开始。
  2. Loading(加载中):当你触发了某个操作,比如生成文本,这个状态表示操作还在进行中,UI会显示个进度条或者加载动画。
  3. Success(成功状态):当操作成功完成,比如文本生成成功了,这个状态就会拿到生成的文本内容,然后展示给用户。
  4. Error(错误状态):如果操作过程中发生了错误,比如网络问题或者其他原因,这个状态会包含错误信息,方便展示给用户知道哪里出错了。

简单来说,这段代码通过不同的状态来控制界面上的显示内容,比如显示加载中、展示结果、或者报错提示,用起来也很灵活。

代码语言:java
复制
package com.nim.nimgeminidemo

val images = arrayOf(
    R.drawable.baked_goods_1,
    R.drawable.baked_goods_2,
    R.drawable.baked_goods_3,
)
val imageDescriptions = arrayOf(
    R.string.image1_description,
    R.string.image2_description,
    R.string.image3_description,
)

@Composable
fun BakingScreen(
    bakingViewModel: BakingViewModel = viewModel()
) {
    val selectedImage = remember { mutableIntStateOf(0) }
    val placeholderPrompt = stringResource(R.string.prompt_placeholder)
    val placeholderResult = stringResource(R.string.results_placeholder)
    var prompt by rememberSaveable { mutableStateOf(placeholderPrompt) }
    var result by rememberSaveable { mutableStateOf(placeholderResult) }
    val uiState by bakingViewModel.uiState.collectAsState()
    val context = LocalContext.current

    Column(
        modifier = Modifier.fillMaxSize()
    ) {
        Text(
            text = stringResource(R.string.baking_title),
            style = MaterialTheme.typography.titleLarge,
            modifier = Modifier.padding(16.dp)
        )

        LazyRow(
            modifier = Modifier.fillMaxWidth()
        ) {
            itemsIndexed(images) { index, image ->
                var imageModifier = Modifier
                    .padding(start = 8.dp, end = 8.dp)
                    .requiredSize(200.dp)
                    .clickable {
                        selectedImage.intValue = index
                    }
                if (index == selectedImage.intValue) {
                    imageModifier =
                        imageModifier.border(BorderStroke(4.dp, MaterialTheme.colorScheme.primary))
                }
                Image(
                    painter = painterResource(image),
                    contentDescription = stringResource(imageDescriptions[index]),
                    modifier = imageModifier
                )
            }
        }

        Row(
            modifier = Modifier.padding(all = 16.dp)
        ) {
            TextField(
                value = prompt,
                label = { Text(stringResource(R.string.label_prompt)) },
                onValueChange = { prompt = it },
                modifier = Modifier
                    .weight(0.8f)
                    .padding(end = 16.dp)
                    .align(Alignment.CenterVertically)
            )

            Button(
                onClick = {
                    val bitmap = BitmapFactory.decodeResource(
                        context.resources,
                        images[selectedImage.intValue]
                    )
                    bakingViewModel.sendPrompt(bitmap, prompt)
                },
                enabled = prompt.isNotEmpty(),
                modifier = Modifier
                    .align(Alignment.CenterVertically)
            ) {
                Text(text = stringResource(R.string.action_go))
            }
        }

        if (uiState is UiState.Loading) {
            CircularProgressIndicator(modifier = Modifier.align(Alignment.CenterHorizontally))
        } else {
            var textColor = MaterialTheme.colorScheme.onSurface
            if (uiState is UiState.Error) {
                textColor = MaterialTheme.colorScheme.error
                result = (uiState as UiState.Error).errorMessage
            } else if (uiState is UiState.Success) {
                textColor = MaterialTheme.colorScheme.onSurface
                result = (uiState as UiState.Success).outputText
            }
            val scrollState = rememberScrollState()
            Text(
                text = result,
                textAlign = TextAlign.Start,
                color = textColor,
                modifier = Modifier
                    .align(Alignment.CenterHorizontally)
                    .padding(16.dp)
                    .fillMaxSize()
                    .verticalScroll(scrollState)
            )
        }
    }
}

@Preview(showSystemUi = true)
@Composable
fun BakingScreenPreview() {
    BakingScreen()
}

(4)解释代码:

  1. imagesimageDescriptions: 定义了一个图片数组和对应的图片描述,用于在界面上显示可供用户选择的图片。
  2. BakingScreen 组件: 这是主界面的显示逻辑,用户在这里可以选择图片并输入文本提示,调用ViewModel生成文本结果。
  3. selectedImage: 记录用户选择的图片索引。
  4. promptresult: 分别用于保存用户输入的提示文字和生成的结果。
  5. uiState: 保存界面的当前状态,比如正在加载、显示成功结果、或者发生错误。
  6. 图片选择区: 用 LazyRow 显示可横向滑动的图片列表,用户点击图片会更新选中的图片,图片会显示带边框的选中状态。
  7. 输入提示与按钮: 用户输入文本提示后,点击按钮,应用会使用 bakingViewModel 调用AI生成文本。
  8. 状态显示: 根据 uiState 的值,界面会显示加载进度、成功生成的文本或者错误信息。
  9. 如果是 Loading 状态,会显示进度指示器。
  10. 如果是 Success 状态,会显示生成的文本。
  11. 如果是 Error 状态,会显示错误消息。

2.6 阅读源码

2.6.1 GenerativeModel 是API的核心类

在这个类中,构造函数接受多个参数:

  • modelName:指定使用哪个AI模型,例如 gemini-pro-vision
  • apiKey:用于认证的API密钥,保证客户端与Google的AI服务能够进行安全的通信。
  • generationConfig:生成配置参数,控制生成内容的特性,例如输出文本的长度或生成图像的分辨率。
  • safetySettings:安全设置,用于控制生成内容时的安全性和过滤规则,防止不合适的内容生成。
  • requestOptions:控制与后端通信时的其他配置,比如超时时间或请求头信息。

2.6.2 重点讲解safetySettings 类

解释代码

代码语言:java
复制
val harassmentSetting = SafetySetting(
    category = SafetyCategory.HARASSMENT,
    setting = SafetyLevel.ONLY_HIGH
)

val harmfulSetting = SafetySetting(
    category = SafetyCategory.HARMFUL_CONTENT,
    setting = SafetyLevel.ONLY_HIGH
)

val hateSetting = SafetySetting(
    category = SafetyCategory.HATE_SPEECH,
    setting = SafetyLevel.ONLY_HIGH
)

val violenceSetting = SafetySetting(
    category = SafetyCategory.VIOLENCE,
    setting = SafetyLevel.ONLY_HIGH
)

作用:

  1. 骚扰 (HARASSMENT):防止生成与骚扰相关的内容。
  2. 有害内容 (HARMFUL_CONTENT):防止生成有可能伤害他人或自我伤害的内容。
  3. 仇恨言论 (HATE_SPEECH):防止生成与仇恨言论相关的内容。
  4. 暴力 (VIOLENCE):防止生成与暴力行为相关的内容。

ONLY_HIGH 设置:

  • ONLY_HIGH 表示只阻止那些被认为高度危害的内容。较轻微的内容可能不会被阻止,适合那些希望保持一定灵活性但仍避免重大违规内容的场景。

三、技术难点

使用Gemini API 时,你可能会遇到几个技术上的挑战。首先是 API Key 的管理。API Key硬编码在代码中是非常不安全的,容易被恶意用户获取并滥用。建议通过环境变量的方式管理API Key,或者使用更高级的密钥管理系统来保护它。

四、学习笔记

  • 如何使用模板快速搭建项目: 通过Gemini API Starter模板,节省了不少配置时间,轻松完成了项目基础搭建。
  • API Key的获取和配置: 在Google AI Studio中生成并管理API Key非常重要。学会了如何安全地将API Key引入项目中。
  • 权限配置与网络通信: 理解了网络通信相关的权限管理,确保应用能够顺利与AI服务器通信。

五、总结

通过这次实践,我们成功构建了一个具备AI功能的Android应用,并了解了如何快速使用Android Studio结合Gemini API进行开发。从项目搭建到API配置,每一步都有详细的操作介绍。希望这篇文章能帮助你快速上手Gemini API,并在自己的项目中集成AI功能。即使是初学者也可以通过本教程,了解在安卓开发中的AI集成基础。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、项目背景
  • 二、项目开发
    • 2.3 添加依赖与权限
      • 2.4 配置API密钥与环境
        • 2.5 讲解核心代码
          • (2).解释代码
          • (3)解释代码:
        • 2.6 阅读源码
          • 2.6.1 GenerativeModel 是API的核心类
            • 2.6.2 重点讲解safetySettings 类
              • 作用:
              • ONLY_HIGH 设置:
          • 三、技术难点
          • 四、学习笔记
          • 五、总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档