在数字时代,聊天机器人已成为一种常见的交互方式。借助 Google 的 Gemini API,我们可以构建功能强大的 Android 聊天机器人应用。本文将深入探讨如何使用 Android Studio、Retrofit 库以及 Material Design 组件,从零开始构建一个简单的聊天机器人,并集成 Google 的 Gemini API。我们将逐步完成以下步骤,并详细解释每个步骤的目的和实现方式,确保你能够轻松上手,打造出自己的智能聊天助手。
项目初始化与依赖添加
首先,我们需要创建一个新的 Android 项目,并引入必要的库,以便进行网络请求和 UI 开发。我们将项目命名为 geminiapi_demo2
。
步骤:
- 创建项目: 在 Android Studio 中,选择 “File” -> “New” -> “New Project”,选择 “Empty Activity” 模板,并设置项目名称、包名等。
- 添加依赖: 打开
build.gradle (Module: app)
文件,在dependencies
块中添加以下依赖:
implementation 'com.squareup.retrofit2:retrofit:2.9.0' // Retrofit 库,用于网络请求
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // Gson 库,用于 JSON 数据解析
implementation 'com.google.android.material:material:1.11.0' // Material Design 组件库,用于构建简单且相对美观的 UI
retrofit
: Retrofit 是一个类型安全的 HTTP 客户端,用于简化网络请求。它允许你以声明式的方式定义 API 接口,并自动处理网络连接、请求序列化和响应反序列化等任务。converter-gson
: Gson 是一个 Java 库,用于将 JSON 数据转换为 Java 对象,反之亦然。Retrofit 使用 Gson 将 Gemini API 返回的 JSON 响应转换为 Java 对象,方便我们在应用中使用。material
: Material Design 组件库提供了各种预定义的 UI 组件,可以帮助我们快速构建美观的 Android 应用。它包括按钮、文本框、卡片视图等常用组件,以及主题和样式,可以提升用户体验。
- 同步 Gradle: 点击 “Sync Now” 按钮,同步 Gradle 文件,下载并安装依赖库。
Retrofit 配置
接下来,我们需要创建一个 Retrofit 客户端类,用于发送 HTTP 请求到 Gemini API。
步骤:
- 创建
ApiClient.java
文件: 在com.example.geminiapi_demo2
包下创建一个名为ApiClient.java
的 Java 文件。 - 添加代码: 将以下代码添加到
ApiClient.java
文件中:
package com.example.geminiapi_demo2;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient {
private static final String BASE_URL = "https://generativelanguage.googleapis.com/v1/"; // Gemini API 的基础 URL
private static Retrofit retrofit;
public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL) // 设置基础 URL
.addConverterFactory(GsonConverterFactory.create()) // 添加 Gson 转换器
.build();
}
return retrofit;
}
}
BASE_URL
: 定义 Gemini API 的基础 URL。这是所有 API 请求的公共地址。Retrofit.Builder()
: 创建 Retrofit 客户端的构建器。通过构建器,我们可以配置 Retrofit 客户端的各种属性,例如基础 URL、转换器等。baseUrl()
: 设置请求的基础 URL。所有 API 请求都将基于此 URL。addConverterFactory()
: 添加 Gson 转换器,用于将 JSON 数据转换为 Java 对象。这使得 Retrofit 可以自动处理 JSON 响应。build()
: 构建 Retrofit 客户端。
Gemini API 接口定义
现在,我们需要定义一个 Java 接口,用于描述与 Gemini API 交互的方法。这个接口将使用 Retrofit 的注解来描述 HTTP 请求的细节。
步骤:
- 创建
GeminiApiService.java
文件: 在com.example.geminiapi_demo2
包下创建一个名为GeminiApiService.java
的 Java 接口文件。 - 添加代码: 将以下代码添加到
GeminiApiService.java
文件中:
package com.example.geminiapi_demo2;
import com.example.geminiapi_demo2.models.GenerateContentRequest;
import com.example.geminiapi_demo2.models.GenerateContentResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface GeminiApiService {
@Headers({
"Content-Type: application/json", // 设置请求头,指定内容类型为 JSON
"x-goog-api-key: YOUR_API_KEY" // 设置请求头,添加 API 密钥
})
@POST("models/{model}:generateContent") // 定义 POST 请求的 URL
Call<GenerateContentResponse> generateContent(@Path("model") String model, @Body GenerateContentRequest request); // 定义请求方法
}
@Headers
: 用于设置请求头。请求头包含了关于请求的元数据,例如内容类型、授权信息等。Content-Type: application/json
:指定请求体的内容类型为 JSON。Gemini API 要求请求体是 JSON 格式。x-goog-api-key: YOUR_API_KEY
:添加 API 密钥,用于身份验证。请务必将YOUR_API_KEY
替换为你实际的 API 密钥。 没有正确的 API 密钥,你将无法访问 Gemini API。
@POST
: 指定请求方法为 POST,并定义请求的 URL。POST 请求用于向服务器提交数据。@Path
: 用于替换 URL 中的占位符{model}
。这允许我们动态地指定要使用的 Gemini 模型。@Body
: 用于指定请求体,这里使用GenerateContentRequest
对象作为请求体。请求体包含了要发送给服务器的数据。Call<GenerateContentResponse>
: 定义请求方法的返回类型,使用 Retrofit 的Call
对象,并指定响应类型为GenerateContentResponse
。Call
对象代表一个异步的 API 请求,GenerateContentResponse
是我们自定义的响应数据模型。
Gemini API 密钥获取
要使用 Gemini API,你需要一个有效的 API 密钥。以下是获取密钥的步骤:
步骤:
- 准备一个谷歌账号,如果已经有了,请跳过。
- 建议使用美国节点访问。
- 打开Google AI Studio 申请api的网址:https://makersuite.google.com/app/apikey。
- 点击左边菜单里的 Get API key,然后在右边点击创建API密钥,这样就可以得到你的API密钥了。
数据模型创建
为了方便地处理 Gemini API 的请求和响应数据,我们需要创建 Java 类来映射这些数据。这些类将定义请求和响应的结构,并允许我们以类型安全的方式访问数据。
步骤:
- 创建
models
包: 在com.example.geminiapi_demo2
包下创建一个名为models
的包。这个包将包含所有的数据模型类。 - 创建
GenerateContentRequest.java
文件: 在models
包下创建一个名为GenerateContentRequest.java
的 Java 文件,用于定义请求数据模型。 - 添加代码: 将以下代码添加到
GenerateContentRequest.java
文件中:
package com.example.geminiapi_demo2.models;
import java.util.List;
public class GenerateContentRequest {
private List<Content> contents;
public GenerateContentRequest(List<Content> contents) {
this.contents = contents;
}
public List<Content> getContents() {
return contents;
}
public void setContents(List<Content> contents) {
this.contents = contents;
}
public static class Content {
private List<Part> parts;
public Content(List<Part> parts) {
this.parts = parts;
}
public List<Part> getParts() {
return parts;
}
public void setParts(List<Part> parts) {
this.parts = parts;
}
}
public static class Part{
private String text;
public Part(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
}
- 这个类定义了 Gemini API 请求的结构,包括
contents
列表,其中包含Content
对象,而Content
对象又包含Part
列表,Part
对象包含text
属性。这种嵌套结构反映了 Gemini API 的请求格式。
- 创建
GenerateContentResponse.java
文件: 在models
包下创建一个名为GenerateContentResponse.java
的 Java 文件,用于定义响应数据模型。 - 添加代码: 将以下代码添加到
GenerateContentResponse.java
文件中:
package com.example.geminiapi_demo2.models;
import java.util.List;
public class GenerateContentResponse {
private List<Candidate> candidates;
public GenerateContentResponse(List<Candidate> candidates) {
this.candidates = candidates;
}
public List<Candidate> getCandidates() {
return candidates;
}
public void setCandidates(List<Candidate> candidates) {
this.candidates = candidates;
}
public static class Candidate{
private Content content;
public Candidate(Content content){
this.content=content;
}
public Content getContent() {
return content;
}
public void setContent(Content content) {
this.content = content;
}
}
public static class Content{
private List<Part> parts;
public Content(List<Part> parts){
this.parts=parts;
}
public List<Part> getParts() {
return parts;
}
public void setParts(List<Part> parts) {
this.parts = parts;
}
}
public static class Part{
private String text;
public Part(String text){
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
}
- 这个类定义了 Gemini API 响应的结构,包括
candidates
列表,其中包含Candidate
对象,而Candidate
对象又包含Content
对象,Content
对象包含Part
列表,Part
对象包含text
属性。这种结构反映了 Gemini API 返回的响应格式。
API 调用和响应处理
现在,我们将学习如何在 MainActivity
中调用 Gemini API,并处理返回的响应数据。
步骤:
- 修改
MainActivity.java
文件: 将以下代码添加到MainActivity.java
文件中:
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.example.geminiapi_demo2.models.GenerateContentRequest;
import com.example.geminiapi_demo2.models.GenerateContentResponse;
import com.example.geminiapi_demo2.models.GenerateContentRequest.Content;
import com.example.geminiapi_demo2.models.GenerateContentRequest.Part;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MainActivity extends AppCompatActivity {
private EditText inputEditText;
private Button sendButton;
private TextView responseTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inputEditText = findViewById(R.id.inputEditText);
sendButton = findViewById(R.id.sendButton);
responseTextView = findViewById(R.id.responseTextView);
sendButton.setOnClickListener(v -> {
String prompt = inputEditText.getText().toString().trim();
if (!prompt.isEmpty()) {
updateUI("User", prompt); // 先显示用户输入
generateContent("gemini-pro", prompt);
inputEditText.setText(""); // 清空输入框
}
});
}
private void generateContent(String model, String prompt) {
GeminiApiService apiService = ApiClient.getClient().create(GeminiApiService.class);
// Prepare the request body
List<Part> parts = new ArrayList<>();
parts.add(new Part(prompt));
List<Content> contents = new ArrayList<>();
contents.add(new Content(parts));
GenerateContentRequest request = new GenerateContentRequest(contents);
// Make the API call
Call<GenerateContentResponse> call = apiService.generateContent(model, request);
call.enqueue(new Callback<GenerateContentResponse>() {
@Override
public void onResponse(Call<GenerateContentResponse> call, Response<GenerateContentResponse> response) {
if (response.isSuccessful()) {
GenerateContentResponse geminiResponse = response.body();
if(geminiResponse != null && geminiResponse.getCandidates() != null && !geminiResponse.getCandidates().isEmpty()) {
String responseText = geminiResponse.getCandidates().get(0).getContent().getParts().get(0).getText();
Log.d("Gemini Response", "Response: " + responseText);
updateUI("Gemini", responseText); // 显示 Gemini 响应
}
else {
Log.e("Gemini Response","No response from Gemini");
updateUI("Gemini", "No response from Gemini");
}
} else {
int statusCode = response.code();
String errorMessage = response.message();
try {
// 如果 response.errorBody() 不为空,尝试获取错误详情
if (response.errorBody() != null) {
String errorBodyString = response.errorBody().string();
Log.e("Gemini Response", "Error: Status Code = " + statusCode + ", Message: " + errorMessage + " , Body: " + errorBodyString);
updateUI("Gemini", "Error: Status Code = " + statusCode + ", Message: " + errorMessage + " , Body: " + errorBodyString);
} else {
Log.e("Gemini Response", "Error: Status Code = " + statusCode + ", Message: " + errorMessage);
updateUI("Gemini", "Error: Status Code = " + statusCode + ", Message: " + errorMessage);
}
} catch (Exception e) {
Log.e("Gemini Response", "Error while parsing error body: " + e.getMessage());
Log.e("Gemini Response", "Error: Status Code = " + statusCode + ", Message: " + errorMessage);
updateUI("Gemini", "Error while parsing error body: " + e.getMessage() + "Error: Status Code = " + statusCode + ", Message: " + errorMessage);
}
}
}
@Override
public void onFailure(Call<GenerateContentResponse> call, Throwable t) {
Log.e("Gemini Response", "Error: " + t.getMessage());
updateUI("Gemini", "Error: " + t.getMessage());
}
});
}
private void updateUI(String sender, String message) {
runOnUiThread(() -> {
String currentText = responseTextView.getText().toString();
String newText = currentText + "\n" + sender + ": " + message;
responseTextView.setText(newText);
});
}
}
findViewById()
: 获取布局文件中 UI 组件的引用。这允许我们在 Java 代码中操作 UI 元素。sendButton.setOnClickListener()
: 设置按钮点击事件监听器。当用户点击发送按钮时,会触发此监听器。- 获取用户输入的文本。我们从
EditText
中获取用户输入的内容。 - 调用
updateUI()
方法显示用户输入。这会在TextView
中显示用户输入的消息。 - 调用
generateContent()
方法发送请求。这会调用 Gemini API 并获取响应。 - 清空输入框。我们清空
EditText
,以便用户可以输入新的消息。
- 获取用户输入的文本。我们从
generateContent()
:- 创建
GeminiApiService
实例。我们使用 Retrofit 创建 Gemini API 接口的实例。 - 创建
GenerateContentRequest
对象,用于构建请求体。请求体包含了要发送给 Gemini API 的数据,例如用户输入的消息。 - 使用 Retrofit 的
enqueue()
方法发送异步请求。异步请求不会阻塞 UI 线程,从而避免应用卡顿。 - 在
onResponse()
方法中处理响应。当 Gemini API 返回响应时,会调用此方法。- 如果请求成功,则从
GenerateContentResponse
中提取响应文本,并调用updateUI()
方法显示 Gemini 响应。我们会解析 Gemini API 返回的 JSON 响应,并提取出生成的文本。 - 如果请求失败,则记录错误信息,并调用
updateUI()
方法显示错误信息。我们会记录错误代码和消息,并将其显示在 UI 上。
- 如果请求成功,则从
- 在
onFailure()
方法中处理请求失败的情况。当网络请求失败时,会调用此方法。我们会记录错误信息,并将其显示在 UI 上。
- 创建
updateUI()
:- 使用
runOnUiThread()
方法在 UI 线程中更新 UI。由于 UI 只能在 UI 线程中更新,因此我们需要使用runOnUiThread()
方法。 - 将新的消息添加到
TextView
中。我们会将发送者和消息添加到TextView
中,以便显示聊天记录。
- 使用
使用 Material Design 组件构建 UI
为了创建一个美观的用户界面,我们将使用 Material Design 组件。Material Design 是一套由 Google 设计的视觉语言,它提供了一致的用户体验。
步骤:
- 创建
activity_main.xml
文件: 将以下代码添加到activity_main.xml
文件
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter your prompt here"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/inputEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/sendButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:text="Send" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="16dp"
android:scrollbars="vertical">
<TextView
android:id="@+id/responseTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:textSize="16sp"
android:text="Response will appear here" />
</ScrollView>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
MaterialCardView
: 创建一个带有圆角和阴影的卡片式布局,用于包裹整个 UI。MaterialCardView
提供了一个视觉上吸引人的容器,可以提升用户体验。TextInputLayout
: 创建一个带有提示文本和轮廓样式的输入框的容器。TextInputLayout
简化了输入框的创建,并提供了一些有用的功能,例如错误提示和计数器。TextInputEditText
: 创建一个多行文本输入框。TextInputEditText
允许用户输入多行文本,这对于聊天机器人应用非常有用。MaterialButton
: 创建一个 Material Design 风格的按钮。MaterialButton
提供了一个视觉上吸引人的按钮,并提供了一些有用的功能,例如涟漪效果。ScrollView
: 创建一个可滚动的视图,用于显示长文本。ScrollView
允许用户滚动查看超出屏幕范围的文本。TextView
: 用于显示 Gemini API 的响应。TextView
用于显示文本信息。
最终效果
通过以上步骤,我们成功构建了一个简单的 Android 聊天机器人应用,并集成了 Gemini API。这个应用可以接收用户输入,发送到 Gemini API,并显示 Gemini 的响应。此外,我们使用了 Material Design 组件来提升用户体验,提供更友好的交互界面。
总结与展望
本文详细介绍了如何使用 Android Studio、Retrofit 和 Material Design 组件构建一个基于 Gemini API 的聊天机器人。这个项目只是一个起点,你可以根据自己的需求进行扩展和改进。例如,你可以添加更好的 UI、支持更多的 Gemini API 功能、添加用户身份验证,以及在自己的 Android 项目中集成 Gemini API 等等。Gemini API 提供了丰富的功能,例如文本生成、图像生成和代码生成,你可以探索这些功能,并将其集成到你的聊天机器人中。随着人工智能技术的不断发展,聊天机器人将在各个领域发挥越来越重要的作用。希望本文能够帮助你入门 Android 聊天机器人开发,并激发你对人工智能的兴趣。掌握了这些技能,你就可以构建出更加智能和强大的应用,为用户提供更好的服务。