Android 聊天机器人:集成 Gemini API 的完整指南

61

在数字时代,聊天机器人已成为一种常见的交互方式。借助 Google 的 Gemini API,我们可以构建功能强大的 Android 聊天机器人应用。本文将深入探讨如何使用 Android Studio、Retrofit 库以及 Material Design 组件,从零开始构建一个简单的聊天机器人,并集成 Google 的 Gemini API。我们将逐步完成以下步骤,并详细解释每个步骤的目的和实现方式,确保你能够轻松上手,打造出自己的智能聊天助手。

项目初始化与依赖添加

首先,我们需要创建一个新的 Android 项目,并引入必要的库,以便进行网络请求和 UI 开发。我们将项目命名为 geminiapi_demo2

步骤:

  1. 创建项目: 在 Android Studio 中,选择 “File” -> “New” -> “New Project”,选择 “Empty Activity” 模板,并设置项目名称、包名等。
  2. 添加依赖: 打开 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 应用。它包括按钮、文本框、卡片视图等常用组件,以及主题和样式,可以提升用户体验。
  1. 同步 Gradle: 点击 “Sync Now” 按钮,同步 Gradle 文件,下载并安装依赖库。

Retrofit 配置

接下来,我们需要创建一个 Retrofit 客户端类,用于发送 HTTP 请求到 Gemini API。

步骤:

  1. 创建 ApiClient.java 文件:com.example.geminiapi_demo2 包下创建一个名为 ApiClient.java 的 Java 文件。
  2. 添加代码: 将以下代码添加到 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 请求的细节。

步骤:

  1. 创建 GeminiApiService.java 文件:com.example.geminiapi_demo2 包下创建一个名为 GeminiApiService.java 的 Java 接口文件。
  2. 添加代码: 将以下代码添加到 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 对象,并指定响应类型为 GenerateContentResponseCall 对象代表一个异步的 API 请求,GenerateContentResponse 是我们自定义的响应数据模型。

Gemini API 密钥获取

要使用 Gemini API,你需要一个有效的 API 密钥。以下是获取密钥的步骤:

步骤:

  1. 准备一个谷歌账号,如果已经有了,请跳过。
  2. 建议使用美国节点访问。
  3. 打开Google AI Studio 申请api的网址https://makersuite.google.com/app/apikey
  4. 点击左边菜单里的 Get API key,然后在右边点击创建API密钥,这样就可以得到你的API密钥了。

数据模型创建

为了方便地处理 Gemini API 的请求和响应数据,我们需要创建 Java 类来映射这些数据。这些类将定义请求和响应的结构,并允许我们以类型安全的方式访问数据。

步骤:

  1. 创建 models 包:com.example.geminiapi_demo2 包下创建一个名为 models 的包。这个包将包含所有的数据模型类。
  2. 创建 GenerateContentRequest.java 文件:models 包下创建一个名为 GenerateContentRequest.java 的 Java 文件,用于定义请求数据模型。
  3. 添加代码: 将以下代码添加到 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 的请求格式。
  1. 创建 GenerateContentResponse.java 文件:models 包下创建一个名为 GenerateContentResponse.java 的 Java 文件,用于定义响应数据模型。
  2. 添加代码: 将以下代码添加到 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,并处理返回的响应数据。

步骤:

  1. 修改 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 设计的视觉语言,它提供了一致的用户体验。

步骤:

  1. 创建 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 聊天机器人开发,并激发你对人工智能的兴趣。掌握了这些技能,你就可以构建出更加智能和强大的应用,为用户提供更好的服务。