前言

大家好!在这篇博客中,我将深入讲解如何使用 Android Studio、Retrofit 库以及 Material Design 组件,从零开始构建一个简单的聊天机器人,并集成 Google 的 Gemini API。我们将逐步完成以下步骤,并详细解释每个步骤的目的和实现方式,让我们开始吧!


1. 项目初始化和依赖添加

目的: 创建一个新的 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 客户端,用于简化网络请求。
    • converter-gson Gson 是一个 Java 库,用于将 JSON 数据转换为 Java 对象,反之亦然。
    • material Material Design 组件库提供了各种预定义的 UI 组件,可以帮助我们快速构建美观的 Android 应用。
  3. 同步 Gradle: 点击 “Sync Now” 按钮,同步 Gradle 文件,下载并安装依赖库。

2. 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。
    • Retrofit.Builder() 创建 Retrofit 客户端的构建器。
    • baseUrl() 设置请求的基础 URL。
    • addConverterFactory() 添加 Gson 转换器,用于将 JSON 数据转换为 Java 对象。
    • build() 构建 Retrofit 客户端。

3. Gemini API 接口定义

目的: 定义一个 Java 接口,用于描述与 Gemini API 交互的方法。

步骤:

  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。
      • x-goog-api-key: YOUR_API_KEY:添加 API 密钥,用于身份验证。请将 YOUR_API_KEY 替换为你实际的 API 密钥。
    • @POST 指定请求方法为 POST,并定义请求的 URL。
    • @Path 用于替换 URL 中的占位符 {model}
    • @Body 用于指定请求体,这里使用 GenerateContentRequest 对象作为请求体。
    • Call<GenerateContentResponse> 定义请求方法的返回类型,使用 Retrofit 的 Call 对象,并指定响应类型为 GenerateContentResponse

4. Gemini API密钥获取

目的: 获取Gemini API的密钥以连接至Gemini

步骤:

  1. 准备一个谷歌账号,已经有了请跳过。

  2. 建议使用美国节点访问。

  3. 打开Google AI Studio 申请api的网址https://makersuite.google.com/app/apikey

  4. 点击左边菜单里的 Get API key,然后在右边点击创建API密钥,这样就可以得到你的API密钥了
    在这里插入图片描述

5. 数据模型创建

目的: 创建 Java 类,用于映射 Gemini API 的请求和响应数据。

步骤:

  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 属性。
  4. 创建 GenerateContentResponse.java 文件:models 包下创建一个名为 GenerateContentResponse.java 的 Java 文件,用于定义响应数据模型。

  5. 添加代码: 将以下代码添加到 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 属性。

6. 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 组件的引用。
    • sendButton.setOnClickListener() 设置按钮点击事件监听器。
      • 获取用户输入的文本。
      • 调用 updateUI() 方法显示用户输入。
      • 调用 generateContent() 方法发送请求。
      • 清空输入框。
    • generateContent()
      • 创建 GeminiApiService 实例。
      • 创建 GenerateContentRequest 对象,用于构建请求体。
      • 使用 Retrofit 的 enqueue() 方法发送异步请求。
      • onResponse() 方法中处理响应。
        • 如果请求成功,则从 GenerateContentResponse 中提取响应文本,并调用 updateUI() 方法显示 Gemini 响应。
        • 如果请求失败,则记录错误信息,并调用 updateUI() 方法显示错误信息。
      • onFailure() 方法中处理请求失败的情况。
    • updateUI()
      • 使用 runOnUiThread() 方法在 UI 线程中更新 UI。
      • 将新的消息添加到 TextView 中。

7. 使用 Material Design 组件构建 UI

目的: 使用 Material Design 组件创建简单且相对美观的用户界面。

步骤:

  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。
    • TextInputLayout 创建一个带有提示文本和轮廓样式的输入框的容器。
    • TextInputEditText 创建一个多行文本输入框。
    • MaterialButton 创建一个 Material Design 风格的按钮。
    • ScrollView 创建一个可滚动的视图,用于显示长文本。
    • TextView 用于显示 Gemini API 的响应。

8. 效果展示

总结

通过以上步骤,我们成功地构建了一个简单的 Android 聊天机器人app,并集成了 Gemini API。这个应用可以接收用户输入,发送到 Gemini API,并显示 Gemini 的响应。我们还添加了多次交互、中文输入支持和滑动显示优化,并使用了 Material Design 组件来提升用户体验。

这个项目只是一个起点,你可以根据自己的需求进行扩展和改进。例如,你可以添加更好的 UI、支持更多的 Gemini API 功能、添加用户身份验证,在自己的Android项目中集成GeminiAPI等等。

希望这篇博客对你有所帮助!如果你有任何问题,请随时留言。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐