Android智能聊天机器人开发实战:RecyclerView+OkHttp+JSON全解析

Android智能聊天机器人开发实战:RecyclerView+OkHttp+JSON全解析 简介这是一份面向安卓开发初学者与课程设计学生的高完成度智能聊天机器人实战项目聚焦SQLite本地存储、HttpURLConnection网络请求、ListView界面展示、GSON数据解析及SharedPreferences持久化等核心技能训练有效解决小白在安卓课设中技术整合难、功能闭环弱的痛点。资源包共624个文件包含140个XML布局与配置文件、123个JSON模拟对话数据、157个Flat资源及14个Java源码文件辅以APK安装包、Gradle构建脚本和完整调试资源总大小17.2MB结构清晰、模块分明便于逐层理解登录流程、用户注册校验、欢迎语随机加载、消息收发逻辑与记住密码机制。已有1698人学习下载配套博文详细拆解了从欢迎页→注册页→登录页→主聊天页的全链路实现涵盖数据库建表语句、HTTP请求封装、Adapter定制、异步响应处理等关键代码段与排错要点是掌握安卓基础开发能力的优质实践样本。1. 项目整体设计与技术选型1.1 核心功能拆解这个智能聊天机器人项目说白了就是让你在 Android 手机上和一个 AI 对话。用户在底部输入框打字、点发送消息气泡出现在聊天列表里然后机器人那边处理完请求再把回答气泡返回来。整个交互模式跟微信聊天一模一样但对话对象换成了一个 AI 接口。作为安卓课设它最大的优势在于“视觉冲击力强、技术覆盖面广、演示效果好”。老师看到的不只是一堆代码而是一个界面完整、交互流畅、能实时对话的 App。而且它的技术栈涵盖了 Android 开发里非常核心的几个模块Activity 生命周期管理、RecyclerView 列表复用、网络请求、JSON 数据解析、异步任务处理、SharedPreferences 本地存储。这些知识点随便挑一个都能在答辩时讲上几分钟。适合人群很明确正在准备安卓课程设计的学生、想练手做完整项目的初学者、以及需要快速凑出一个有质量的作品但不想纯抄代码的人。这个项目不是说让你直接克隆一个成品交差而是希望你能跟着思路理解每一层在干什么最后能自己讲清楚。1.2 为什么选择这种技术方案先说结论MVP 方案 RecyclerView OkHttp/Retrofit 第三方智能对话 API SharedPreferences。为什么不自己做 NLP 和语义理解完全没有必要。现在的智能对话 API 已经很成熟了你只需要把用户输入的文字发过去对方返回一段 AI 生成的回答文本你负责把这段文字显示到界面上就行。课设的核心是安卓端开发能力不是训练一个模型这一点一定要想清楚不然会把自己坑死。为什么网络层用 OkHttp 或者 Retrofit因为它们是目前安卓开发的事实标准网上资料多、问题好搜、老师在答辩时看到这两个库也知道你是学过现代开发实践的。而且它们内部封装好了线程切换和请求管理比原生 HttpURLConnection 省心太多。RecyclerView 的选择也是一样的道理——它是现在列表展示的标配。虽然 ListView 也能写但 RecyclerView 在性能、扩展性和面试/答辩印象分上都碾压对手。既然做课设为什么不顺便把主流技术练一遍1.3 项目目录与包结构规划动手写代码之前先把项目结构规划好。这是我个人的习惯也建议你养成一个清晰的包结构不仅让你自己写代码不迷路也会让老师在检查代码时觉得你“有工程意识”。com.example.smartchatbot ├── MainActivity.java // 主入口负责聊天界面 ├── adapter/ │ └── ChatAdapter.java // RecyclerView适配器 ├── bean/ │ └── ChatMessage.java // 消息实体类 ├── api/ │ └── ChatApiService.java // 定义接口请求 ├── net/ │ └── ApiClient.java // 网络客户端封装 └── util/ └── ChatHistoryUtil.java // 历史记录工具类如果你用的是 Kotlin目录结构类似只是文件后缀变成 .kt。我这里用 Java 来写原因有两条一是大多数安卓课设的教学语言仍然是 Java二是 Java 版本的可读性对普通学生来说更友好里面所有的类名和方法名一眼就能看出干什么用的。Kotlin 写起来代码量更少但如果你不是特别熟答辩时被老师追问反而容易卡壳。2. 环境配置与工程创建2.1 开发环境准备要点Android Studio 的安装本身不算复杂但有几个坑我先帮你踩了。首先是版本现在官网下载的版本基本都在 Android Studio 4.x 以上甚至更新老教程里提到的 HAXM 安装已经在多数新版本里不再需要了——因为新版模拟器在 Windows 上默认走的是 WHPX 或者 AEHD你不需要手动去装 HAXM 驱动。如果遇到模拟器无法启动、报错提示“HAXM is not installed”先别急着装检查一下 SDK Manager 里系统镜像是否下载完整、BIOS 里虚拟化是否开启比折腾 HAXM 靠谱得多。JDK 环境方面Android Studio 新版本内置了 JBRJetBrains Runtime所以你不需要单独配置 JAVA_HOME 也能正常开发。但如果你电脑上有旧版 JDK建议在安装时就别选默认的捆绑路径免得两个环境打架。下载 SDK 组件时国内网络可能会比较慢这里有一个经验在 SDK Manager 的 HTTP Proxy 设置里配置镜像源你搜一下“阿里云安卓SDK镜像”就能找到地址下载速度能快好几倍。模拟器如果是配置不太高的电脑建议直接用真机调试。打开手机开发者模式、开启USB调试插上数据线Android Studio 能直接识别到设备。我之前带过不少同学做课设一大半人卡在了“模拟器卡死”上而真机的运行流畅度和调试体验都会好很多。2.2 创建项目与引入依赖创建项目时选择“Empty Views Activity”。为什么不用 Compose因为课设阶段老师大概率更熟悉传统 XML 布局你自己调试报错也更容易搜到现成答案。Compose 是未来趋势没错但课设不是创新大赛稳定交付比炫技重要。项目创建完成后打开build.gradleModule 级别不是 Project 级别在 dependencies 里加上你要用的库。以我推荐的方案为例需要引入implementation androidx.recyclerview:recyclerview:1.3.2 implementation com.squareup.okhttp3:okhttp:4.12.0 implementation com.google.code.gson:gson:2.10.1三个库各司其职RecyclerView 负责聊天气泡列表OkHttp 负责网络请求Gson 负责把机器人返回的 JSON 翻译成对象。依赖版本不要去死记硬背上面我给的版本号是目前比较新的稳定版。你可以在 build.gradle 里写好后同步如果提示有新版本它会弹窗告诉你。用旧版本其实也能跑别为了追新版本反复折腾。注意引入依赖后一定要点一下“Sync Now”等它同步完成再做下一步。很多新手写完了代码发现到处标红九成是 Gradle 没有同步成功。2.3 网络权限与明文流量设置这一步容易被忽略但它直接决定你能不能联网。在AndroidManifest.xml中manifest标签下、application标签前加上网络权限uses-permission android:nameandroid.permission.INTERNET /如果你调试用的 API 接口不是 HTTPS 而是 HTTP不少免费接口都是 HTTPAndroid 9API 28之后默认禁止明文流量请求会直接报 “Cleartext HTTP traffic not permitted”。解决办法是在application标签里加上android:usesCleartextTraffictrue虽然这样会在正式上线时有安全隐患但做课设调试足够了。如果你有洁癖也可以创建一个network_security_config.xml只对特定域名放开明文限制但我觉得对于课设来说全局允许明文流量是最快、最省事的路径。答辩时如果老师问到这一点你只要能说出原因——“开发调试阶段方便测试”就已经加分了。3. 聊天界面搭建与适配器实现3.1 主界面布局设计聊天界面的布局目标只有一个像一个聊天工具。顶部是标题栏显示“智能聊天机器人”中间是消息列表底部是输入框加发送按钮。用 LinearLayout 做垂直方向排列中间的 RecyclerView 用 weight1 占据剩余空间底部输入区域固定在页面底部。activity_main.xml的简化结构如下LinearLayout android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical androidx.appcompat.widget.Toolbar android:idid/toolbar android:layout_widthmatch_parent android:layout_height?attr/actionBarSize android:title智能聊天机器人 / androidx.recyclerview.widget.RecyclerView android:idid/rvChatList android:layout_widthmatch_parent android:layout_height0dp android:layout_weight1 android:padding10dp android:clipToPaddingfalse / LinearLayout android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationhorizontal android:padding8dp EditText android:idid/etInput android:layout_width0dp android:layout_heightwrap_content android:layout_weight1 android:hint请输入您的问题 android:maxLines4 android:imeOptionsactionSend android:inputTypetext / Button android:idid/btnSend android:layout_widthwrap_content android:layout_heightwrap_content android:text发送 / /LinearLayout /LinearLayout这里有一个细节值得提一下android:imeOptionsactionSend配合android:inputTypetext能让软键盘的右下角回车键变成“发送”按钮。用户体验会好很多你只需要在代码里给 EditText 设置setOnEditorActionListener就能实现键盘发送。这是很多课设里看不到的小细节加上它作品质感会明显提升一个档次。3.2 消息实体类聊天界面要显示两种消息用户发的靠右和机器人回的靠左。为了让 RecyclerView 知道怎么渲染先定义一个实体类public class ChatMessage { private String message; // 消息内容 private int type; // 0用户消息, 1机器人回复 private long time; // 发送时间 public static final int TYPE_USER 0; public static final int TYPE_ROBOT 1; public ChatMessage(String message, int type) { this.message message; this.type type; this.time System.currentTimeMillis(); } public String getMessage() { return message; } public void setMessage(String message) { this.message message; } public int getType() { return type; } public void setType(int type) { this.type type; } public long getTime() { return time; } }不要小看这个简单类。它的设计决定了后续所有逻辑的处理方式。type 字段是关键——不同类型的消息在适配器里走不同的渲染分支。time 字段可以让你在后续扩展中显示消息时间、按时间排序现在用不到但预留它不会给你带来额外成本。3.3 气泡列表项的布局需要准备两个布局文件item_message_user.xml和item_message_robot.xml。两个布局的结构几乎一样区别只在气泡的背景颜色和对齐方向。用户消息靠右、蓝色背景机器人消息靠左、白色或浅灰色背景。这里给出用户消息的布局做参考LinearLayout android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationhorizontal android:padding6dp android:gravityright TextView android:idid/tvContent android:layout_widthwrap_content android:layout_heightwrap_content android:backgrounddrawable/bg_chat_user android:padding12dp android:textSize16sp android:textColor#FFFFFF android:lineSpacingExtra4dp / /LinearLayout机器人消息的布局把gravity改成left背景换成bg_chat_robot文字颜色改成深色即可。背景的圆角气泡需要画 drawable 文件。bg_chat_user.xml示例shape xmlns:androidhttp://schemas.android.com/apk/res/android solid android:color#4A90D9 / corners android:radius16dp / /shape单独做这种 drawable 看起来不起眼但它是气泡效果的灵魂。我见过很多人直接给 TextView 设一个纯色背景出来的效果特别生硬。加一个圆角整个界面立刻就有“聊天 App”的感觉了。也可以再加一层stroke来描边但课设阶段不需要那么细。3.4 适配器的完整实现ChatAdapter 的写法是聊天项目的核心难点之一。关键点有两个一是 RecyclerView 需要多个 ViewType 来区分左右气泡二是消息列表要自动滚动到底部让最新消息可见。public class ChatAdapter extends RecyclerView.AdapterChatAdapter.ChatViewHolder { private ListChatMessage messageList; public ChatAdapter(ListChatMessage messageList) { this.messageList messageList; } Override public int getItemViewType(int position) { return messageList.get(position).getType(); } NonNull Override public ChatViewHolder onCreateViewHolder(NonNull ViewGroup parent, int viewType) { View view; if (viewType ChatMessage.TYPE_USER) { view LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_message_user, parent, false); } else { view LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_message_robot, parent, false); } return new ChatViewHolder(view); } Override public void onBindViewHolder(NonNull ChatViewHolder holder, int position) { ChatMessage chatMessage messageList.get(position); holder.tvContent.setText(chatMessage.getMessage()); } Override public int getItemCount() { return messageList.size(); } static class ChatViewHolder extends RecyclerView.ViewHolder { TextView tvContent; ChatViewHolder(NonNull View itemView) { super(itemView); tvContent itemView.findViewById(R.id.tvContent); } } }看懂这段代码的逻辑了吗getItemViewType返回值决定 RecyclerView 用哪个 item 布局onCreateViewHolder里的viewType参数就是它返回的值。你在 Activity 里创建一条消息时设置 type 为ChatMessage.TYPE_USER或TYPE_ROBOT适配器就能正确地渲染出靠右靠左的布局。自动滚动到底部在 Activity 里写一个方法private void scrollToBottom() { if (chatAdapter.getItemCount() 0) { recyclerView.smoothScrollToPosition(chatAdapter.getItemCount() - 1); } }每次添加新消息时调用它。注意 smoothScrollToPosition 是平滑滚动体验好一些但数据量大时有可能滚得慢。另一种方式是recyclerView.scrollToPosition(chatAdapter.getItemCount() - 1)这个会直接跳到末尾更稳。我实测下来课设这种对话规模用 scrollToPosition 就够了。4. 网络请求层与AI接口接入4.1 接口选型与密钥申请这一步是很多同学最容易迷路的地方我到底该用什么接口下面列出几种常见选项你可以按自己的条件选择。目前比较主流的选择是 OpenAI ChatGPT 接口、国内的智谱AI GLM 接口、百度文心一言、讯飞星火。国外接口的稳定性在这里不做讨论我对课设项目最稳妥的建议是优先考虑国内可直接访问的、有免费额度的平台。智谱的开放平台注册后会送 token对于课设的演示量级完全够用接口调用方式也是标准的 HTTP POST JSON非常适合入门。当然也有完全不注册接口、直接用现成聚合 API 的零代码路线——很多第三方平台提供“聊天机器人 API”的一行调用。但这样一来你代码里就少了一段“组装请求体、解析响应”的逻辑答辩时能讲的内容就少了。除非你时间实在来不及否则我建议至少把请求和解析的代码写在自己项目里。申请密钥的流程基本上是注册账号、实名认证、创建一个应用、拿到 API Key。不同平台的界面不一样但核心都是这个步骤。拿到密钥后建议先在你电脑上用 Postman 或者直接在浏览器里用调试工具测一下接口确认能返回数据后再写安卓代码。这个习惯能帮你把“网络配置问题”和“代码逻辑问题”分开排查省去一大半的调试时间。4.2 OkHttp网络请求封装这里有个原则需要先说明网络请求是耗时操作绝对不能写在主线程UI线程里。OkHttp 的enqueue方法内部会开启子线程执行请求执行完后自动回到主线程回调这是它比 HttpURLConnection 好用的核心原因之一——你不需要手动去写 Handler 切换线程。先创建一个ApiClient类获取 OkHttp 的全局实例public class ApiClient { private static final String BASE_URL https://your-api-endpoint.example.com/v1/chat/completions; private static OkHttpClient client; public static OkHttpClient getClient() { if (client null) { client new OkHttpClient.Builder() .connectTimeout(15, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build(); } return client; } }超时时间在这里值得讲究。连接超时设 15 秒读取超时设 30 秒。AI 接口的响应速度通常比普通接口慢——因为服务端需要真正“思考”生成文本读超时设短了容易误杀正常请求。我一开始曾把 readTimeout 设为 10 秒结果经常报 SocketTimeoutException后来调到 30 秒就稳定多了。接着写发送消息的方法。我们以智谱GLM接口格式为例实际字段名以你用的API文档为准我这里写的是通用的风格public static void sendMessage(String userInput, Callback callback) { String apiKey 你的API_KEY; JSONObject requestBody new JSONObject(); try { JSONObject message new JSONObject(); message.put(role, user); message.put(content, userInput); JSONArray messages new JSONArray(); messages.put(message); requestBody.put(model, glm-4-flash); requestBody.put(messages, messages); } catch (JSONException e) { e.printStackTrace(); } Request request new Request.Builder() .url(BASE_URL) .addHeader(Authorization, Bearer apiKey) .addHeader(Content-Type, application/json) .post(RequestBody.create(requestBody.toString(), MediaType.parse(application/json; charsetutf-8))) .build(); getClient().newCall(request).enqueue(callback); }这个 JSON 组装过程非常直观messages是一个数组里面只有一个对象包含 role哪个人说的和 content说的什么。这就是对话接口的标准格式。更进一步你可以把历史消息都放进 messages 数组里这样机器人就能“记住”之前的对话但课设阶段只发送当前这一条也完全够用。4.3 解析响应并返回UI接口响应也是一个 JSON 结构不同的服务商字段各有不同但大体都长这样{ choices: [ { message: { role: assistant, content: 你好我是你的智能助手。 } } ] }在回调的onResponse里你要把这个嵌套结构一层层剥出来client.newCall(request).enqueue(new Callback() { Override public void onFailure(Call call, IOException e) { // 子线程不能直接更新UI runOnUiThread(() - onError(网络请求失败请检查网络连接)); } Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful() response.body() ! null) { String jsonStr response.body().string(); JSONObject jsonObject new JSONObject(jsonStr); JSONArray choices jsonObject.getJSONArray(choices); String reply choices.getJSONObject(0) .getJSONObject(message) .getString(content); ChatMessage robotMsg new ChatMessage(reply, ChatMessage.TYPE_ROBOT); runOnUiThread(() - { messageList.add(robotMsg); chatAdapter.notifyItemInserted(messageList.size() - 1); scrollToBottom(); }); } else { runOnUiThread(() - onError(服务器返回错误: response.code())); } } });这里必须强调onResponse回调本身是在子线程里执行的你绝对不能直接在里面操作 RecyclerView。上面的代码用了runOnUiThread把 UI 更新切回主线程这是这节课最重要的线程纪律。忘了这一条你会看到各种Only the original thread that created a view hierarchy can touch its views的崩溃日志而且没有任何提示告诉你该怎么改。5. 交互逻辑、状态管理与本地存储5.1 主Activity的完整逻辑串联MainActivity 里要做的事就是把前面所有模块串起来。我列出核心的流程你照着搭就行public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView; private EditText etInput; private Button btnSend; private ListChatMessage messageList; private ChatAdapter chatAdapter; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); initListener(); } private void initView() { recyclerView findViewById(R.id.rvChatList); etInput findViewById(R.id.etInput); btnSend findViewById(R.id.btnSend); LinearLayoutManager layoutManager new LinearLayoutManager(this); layoutManager.setStackFromEnd(true); recyclerView.setLayoutManager(layoutManager); } private void initData() { messageList new ArrayList(); // 启动时加入一条机器人欢迎语 messageList.add(new ChatMessage(你好我是智能聊天机器人有什么可以帮你的吗, ChatMessage.TYPE_ROBOT)); chatAdapter new ChatAdapter(messageList); recyclerView.setAdapter(chatAdapter); scrollToBottom(); } private void initListener() { btnSend.setOnClickListener(v - sendMessage()); etInput.setOnEditorActionListener((v, actionId, event) - { if (actionId EditorInfo.IME_ACTION_SEND) { sendMessage(); return true; } return false; }); } }setStackFromEnd(true)这个方法值得单独说一下。它的作用是让 RecyclerView 从底部开始显示内容——当消息数量不满一屏时列表会靠底而不是靠顶这非常符合聊天 App 的视觉习惯。另一个相关方法smoothScrollToPosition负责在消息增多时把视图推进到底部。这两个配合使用聊天界面的滚动手感就对了。5.2 发送消息的细节处理sendMessage()是核心逻辑方法里面要注意几个容易被忽略的点private void sendMessage() { String content etInput.getText().toString().trim(); if (content.isEmpty()) { Toast.makeText(this, 请输入内容, Toast.LENGTH_SHORT).show(); return; } // 先发用户消息 ChatMessage userMsg new ChatMessage(content, ChatMessage.TYPE_USER); messageList.add(userMsg); chatAdapter.notifyItemInserted(messageList.size() - 1); scrollToBottom(); etInput.setText(); // 再请求机器人回复 requestRobotReply(content); }第一个坑内容为空时不能发送。用trim()判断能过滤掉用户只输入空格的情况。顺手弹个 Toast 提示体验会好很多。第二个坑清空输入框要放在用户消息发送成功之后立即执行而不是等网络响应回来后再清。有些新手会把etInput.setText()写在回调里结果用户连点几次发送输入框还是上次的内容体验很差。第三个坑连续快速点击发送怎么办比如用户一口气点了三次发送按钮。如果每次点击都发请求不仅浪费资源还可能在消息列表里出现乱序。简单处理方式是在机器人没有回复之前把发送按钮设置为不可点击收到回复后再恢复。这一条属于加分项但很能体现你考虑问题全面。private void setInputEnabled(boolean enabled) { btnSend.setEnabled(enabled); etInput.setEnabled(enabled); }5.3 请求失败与异常兜底网络请求不可能永远成功。WiFi 断了、服务器超时、API 额度用完这些情况你都应该在界面上给出反馈而不是让 App 卡在那里。我习惯用一个通用的错误处理方法private void handleError(String errorMsg) { ChatMessage errorMsgObj new ChatMessage(errorMsg, ChatMessage.TYPE_ROBOT); messageList.add(errorMsgObj); chatAdapter.notifyItemInserted(messageList.size() - 1); scrollToBottom(); }这样用户看到的是机器人发来一条“抱歉我刚才走神了请稍后再试”之类的消息而不是毫无反应。从产品角度说这叫“优雅降级”。从答辩角度说老师会觉得你考虑到了真实场景中的边界情况。还有个小技巧如果在请求期间用户退出了 App回调回来时 Activity 可能已经销毁。你可以用isFinishing()或isDestroyed()来判断当前 Activity 是否还在不在就直接 return。否则就会出现内存泄漏或者View not attached to window manager崩溃。5.4 历史记录保存与加载课设做到这个程度已经称得上“完整”了。但如果想让作品再上一档我强烈建议加入历史记录功能——App 重启后之前的聊天内容还在。实现方式用 SharedPreferences 序列化成 JSON 字符串就行不需要 SQLite因为课设的数据量不大SQLite 显得杀鸡用牛刀。public class ChatHistoryUtil { private static final String PREFS_NAME chat_history; private static final String KEY_MESSAGES messages; public static void saveHistory(Context context, ListChatMessage messageList) { SharedPreferences prefs context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); JSONArray jsonArray new JSONArray(); for (ChatMessage msg : messageList) { JSONObject obj new JSONObject(); try { obj.put(message, msg.getMessage()); obj.put(type, msg.getType()); obj.put(time, msg.getTime()); jsonArray.put(obj); } catch (JSONException e) { e.printStackTrace(); } } prefs.edit().putString(KEY_MESSAGES, jsonArray.toString()).apply(); } public static ListChatMessage loadHistory(Context context) { SharedPreferences prefs context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); String json prefs.getString(KEY_MESSAGES, []); ListChatMessage list new ArrayList(); try { JSONArray jsonArray new JSONArray(json); for (int i 0; i jsonArray.length(); i) { JSONObject obj jsonArray.getJSONObject(i); ChatMessage msg new ChatMessage(obj.getString(message), obj.getInt(type)); msg.setTime(obj.getLong(time)); list.add(msg); } } catch (JSONException e) { e.printStackTrace(); } return list; } }调用时机在initData()里先尝试加载历史记录没有历史记录时才添加欢迎语在每次收到机器人回复后调用saveHistory保存。这样整个 App 的体验就“闭环”了。6. 常见问题、调试经验与答辩准备6.1 高频报错速查表我把这个项目里最常出现的问题整理成一张表每一项都是我亲眼见过有人踩过的报错信息原因解决办法Cleartext HTTP traffic not permittedAPI地址是HTTP明文系统默认禁止在Manifest里加android:usesCleartextTraffictrueOnly the original thread that created a view hierarchy can touch its views在子线程更新了UI用runOnUiThread包裹UI操作SocketTimeoutException网络超时调大OkHttp的readTimeout检查接口是否真实可用JSONException: No value for content响应格式跟你预期的不一样先把返回的JSON字符串打印出来逐层对比字段名ClassNotFoundException引入了库但没有同步检查Gradle是否Sync成功尝试Build - Rebuild ProjectRecyclerView: No adapter attached忘记设置adapter在setLayoutManager后调用setAdapter排查网络类问题我有一个固定的调试套路先把response.body().string()打印到 Logcat 里看一眼真实的返回结构再决定代码怎么解析。很多人一遇到解析失败就去改代码猜字段属实干得最累的一种方式。用 Log 打出来一切一目了然。6.2 项目答辩时的加分讲解点代码写完之后你还要准备答辩。很多同学代码能跑但被老师一问就卡壳主要是没有提前准备好“讲什么、怎么讲”。我帮你梳理好了这个项目值得重点讲的东西就这几块第一RecyclerView 的数据驱动机制。你就讲“消息列表的数据源是一个 List里面的每条消息有 type 字段适配器根据 type 决定渲染哪个布局”。这句话一出来老师就知道你真懂列表复用的原理。第二异步线程模型。你就讲“网络请求在子线程执行完成后通过 runOnUiThread 切回主线程更新 UI避免 ANR”。这是 Android 并发最核心的考点。第三请求体的 messages 结构。你就讲“这个机器人不是无状态的一问一答而是把对话历史放进 messages 数组里让大模型理解上下文”。这一段话能体现你对自己调用接口的理解深度。第四生命周期管理。如果老师问“如果在请求中退出了 Activity会怎么样”你就说“会检查 isFinishing 或者 onDestroy 时释放资源避免内存泄漏”。这是加分项中的加分项。6.3 从课设到个人项目的扩展建议如果你做完这个项目还有余力试试在它上面加一两个功能随便加一个都能让作品脱颖而出。比如语音输入用SpeechRecognizer把语音转成文字再发出去流式输出用 SSE 协议让机器人的回答一个字一个字蹦出来效果极其炫酷多轮对话记忆把历史消息存进数据库支持随时清空记录主题换肤给聊天界面加一个黑夜模式。其中“流式输出”的视觉冲击力最强因为它会让老师觉得你不是调了个接口那么简单而是在做真正的产品体验。但复杂度也确实高一些需要你对 OkHttp 的事件源和文本拼接有较深的理解。我自己第一次实现流式输出时因为没处理好半截 UTF-8 字符的解码问题中文最后总有几个乱码字这个坑踩过一次就长记性了解决方式是改用source()按字符读取再拼接到缓冲区而不是按字节去截断。语音输入相比起来要简单很多SpeechRecognizer 的 API 在官方文档里都有现成示例你只需在 AndroidManifest 里声明RECORD_AUDIO权限然后加一个麦克风按钮。这个功能演示时也特别抓眼球——对着手机说一句话文字出现在输入框里点发送机器人回答。整套交互跟微信语音输入一样顺对课设评分来说完全是碾压级的体验。7. 最后的工程化建议项目写完了代码也能跑了接下来还有几件收尾的小事别落下。第一测试几个典型的对话场景——机器人自我介绍、进行简单的数学计算、帮你写一段文案、说个冷笑话保证在答辩演示时不出丑。千万不要现场演示时连了教室的公用WiFi后请求超时这种事故太致命了。我的建议是你答辩前用手机个人热点做演示备份出现网络问题马上切热点救场。第二整理一下项目目录结构。删掉无用的注释、调试代码和未使用的资源文件。有些同学的代码里有一堆注释掉的旧代码老师一眼就能看出你是在网上复制粘贴之后东删西改的。代码整洁度是印象分的重要组成部分。第三在你的代码关键位置加中文注释。不是那种“定义了一个变量”的废话注释而是要写清楚“这个方法的职责是什么、为什么这么设计”。比如适配器里getItemViewType上方写一行“根据消息类型返回不同的布局ID实现左右气泡区分”。你答辩时能照着注释讲老师不会觉得你在死记硬背。我做了这么多年项目见过很多同学把精力花在找“最全的源码包”上而不是把核心代码理解透。课设这件事表面上拼的是代码能不能跑实际拼的是你能不能把它讲清楚。智能聊天机器人这个选题最大的幸福之处就在于——它的技术链条是完整且清晰的UI、数据模型、列表渲染、网络请求、异步线程、本地持久化。你把整条链路亲手打通一遍结课之后你会发现之后再去做别的 App很多底层逻辑都是相通的。这个项目带来的收获远不止一个分数。本文还有配套的精品资源点击获取