- 前端 UI 数据展示异常(核心问题)
后端接口 /api/auth/me 始终返回完整的用户数据(包括 phone="88888888888"、birthday="2005-08-08"、gender="1" 等字段),响应状态码均为 200,但 Android 端个人信息页面(PersonalInfoActivity)中,手机号、生日、性别等字段始终显示 “未设置”,仅用户名和角色能正常展示。从日志可见,前端解析时大部分字段(如 userId、realName、phone)被解析为空值,仅少数字段有效。
- 非致命性请求异常
多次出现 “获取用户信息失败:Canceled” 日志,表现为重复发送 /api/auth/me 请求后,前一次请求被取消。但该异常未影响功能使用,因前端有本地缓存兜底,UI 未出现崩溃或卡顿。
- 功能扩展需求
需要在现有 Android 项目中实现类似微信的发消息功能,涵盖单聊 / 群聊界面、消息收发、聊天记录存储、消息状态同步(已发送 / 已读)等核心能力。
二、问题排查与解决思路
(一)UI 数据展示异常的排查思路
先确认后端数据有效性:从日志中 okhttp.OkHttpClient 的响应体可知,后端返回的 JSON 数据完整,字段名和值均正确,排除后端数据缺失问题。
聚焦前端解析流程:日志显示 “UserInfo 为 null,尝试直接从 UserResponse 获取字段”,说明前端解析时未正确处理后端的嵌套数据结构(后端数据嵌套在 data.userInfo 中),且可能存在字段映射或类型不匹配。
验证 UI 赋值逻辑:前端解析后字段为空,导致 UI 组件接收空值并显示 “未设置”,需检查字段赋值代码是否正确关联解析后的有效数据。
(二)请求 Canceled 异常的排查思路
分析请求触发场景:日志中 “页面重新可见”“防抖机制触发” 与请求取消同时出现,推测是页面切换时,前一次未完成的网络请求被主动取消,避免重复请求导致资源浪费。
评估影响范围:该异常仅为请求层面的优化处理,前端通过缓存兜底保障了 UI 正常展示,无功能中断,属于 “良性异常”,无需紧急修复,但可优化请求管理逻辑。
(三)发消息功能的实现思路
拆解核心模块:微信发消息功能本质是 “实时通信 + 数据存储 + UI 展示 + 状态同步”,需按 “数据模型→UI 搭建→消息收发→本地存储→状态管理” 的顺序逐步实现。
技术选型适配:结合现有项目使用的 Retrofit、OkHttp,优先选用 OkHttp 的 WebSocket 实现实时通信,Room 数据库存储聊天记录,确保技术栈一致性,降低集成成本。
三、具体解决方法
(一)解决 UI 数据展示异常
修正数据模型类(字段映射与结构对齐)后端返回数据结构为 {data: {userInfo: {id, phone, birthday, ...}}},前端需定义对应的嵌套模型类,确保字段名、类型与后端完全一致:
kotlin
// 外层响应模型
data class UserResponse(
val code: Int,
val message: String,
val data: UserData, // 嵌套数据层
val timestamp: Long,
val success: Boolean
)
// 数据层模型(包含userInfo)
data class UserData(
val accessToken: String?,
val refreshToken: String?,
val userInfo: UserInfo, // 核心用户信息对象
val expiresIn: Long?
)
// 核心用户信息模型(字段与后端完全对齐)
data class UserInfo(
val id: Int, // 后端为int类型,前端避免用String
val username: String,
val realName: String,
val phone: String?,
val address: String?,
val birthday: String?,
val gender: String?, // 后端为"1"/"2"字符串
val role: String
)
修正解析与 UI 赋值逻辑从嵌套的 userInfo 对象中读取字段,避免直接从外层 UserResponse 读取,并处理空值和枚举类型转换:
kotlin
// 接口请求成功回调中解析数据
fun onUserInfoSuccess(response: UserResponse) {
val userInfo = response.data.userInfo // 正确获取嵌套的用户信息
// 手机号赋值(空值兜底)
tvPhone.text = userInfo.phone ?: "未设置手机号"
// 生日赋值
tvBirthday.text = userInfo.birthday ?: "未设置"
// 性别赋值(处理"1"/"2"枚举含义)
tvGender.text = when (userInfo.gender) {
"1" -> "男"
"2" -> "女"
else -> "未设置"
}
// 其他字段(姓名、地址等)同理赋值
}
优化缓存同步逻辑确保接口获取新数据后,先更新本地缓存,再渲染 UI,避免旧缓存覆盖新数据:
kotlin
// 缓存新数据到SharedPreferences
fun cacheUserInfo(userInfo: UserInfo) {
val editor = sharedPreferences.edit()
editor.putInt("userId", userInfo.id)
editor.putString("phone", userInfo.phone)
editor.putString("birthday", userInfo.birthday)
editor.putString("gender", userInfo.gender)
editor.apply()
}
// 读取缓存时优先使用新数据,无新数据再读缓存
fun loadUserInfo() {
val userInfo = getFromApi() // 接口获取新数据
if (userInfo != null) {
cacheUserInfo(userInfo) // 更新缓存
updateUI(userInfo) // 用新数据渲染UI
} else {
// 接口失败时读取缓存
val phone = sharedPreferences.getString("phone", "未设置")
tvPhone.text = phone
}
}
(二)优化请求 Canceled 异常(可选,不影响功能)
统一请求管理:使用 CompositeDisposable(RxJava)或 CoroutineScope 管理网络请求,页面销毁 / 切换时统一取消未完成请求,避免重复取消日志:
kotlin
// 用CoroutineScope管理请求
private val requestScope = CoroutineScope(Dispatchers.IO)
// 发起请求时
fun fetchUserInfo() {
requestScope.launch {
val response = api.getUserInfo()
// 处理响应
}
}
// 页面销毁时取消所有请求
override fun onDestroy() {
super.onDestroy()
requestScope.cancel()
}
调整防抖策略:延长防抖间隔(如从 500ms 改为 1000ms),避免频繁触发 “请求过于频繁” 导致的请求取消。
(三)实现类似微信的发消息功能
设计核心数据模型
kotlin
// 消息模型(存储单条消息信息)
data class Message(
val id: String, // 唯一标识(UUID生成)
val conversationId: String, // 会话ID(单聊:用户1_用户2;群聊:群ID)
val senderId: Int, // 发送者ID(关联用户ID)
val receiverId: Int, // 接收者ID(单聊)/群ID(群聊)
val content: String, // 文本内容(图片存URL)
val type: Int, // 消息类型:文本(1)、图片(2)
val status: Int, // 状态:发送中(0)、已发送(1)、已读(2)、失败(-1)
val timestamp: Long // 发送时间戳
)
// 会话模型(聊天列表展示)
data class Conversation(
val id: String,
val otherSideId: Int, // 对方用户ID/群ID
val otherSideName: String, // 对方昵称/群名
val otherSideAvatar: String?, // 头像URL
val lastMessage: String, // 最后一条消息内容
val unreadCount: Int, // 未读数量
val lastTime: Long // 最后消息时间
)
搭建 UI 界面
聊天列表页:用 RecyclerView 展示会话,每个 Item 包含头像、昵称、最后一条消息、未读红点、时间。
聊天详情页:顶部为对方信息 + 返回按钮,中间用 RecyclerView 展示消息气泡(区分发送方 / 接收方),底部为输入框 + 发送 / 图片按钮。
实现消息收发(WebSocket 实时通信)
kotlin
// 初始化WebSocket连接(全局单例)
private fun initWebSocket() {
val request = Request.Builder().url("ws://你的服务器地址/chat").build()
val webSocket = OkHttpClient().newWebSocket(request, object : WebSocketListener() {
// 接收服务器推送的消息
override fun onMessage(webSocket: WebSocket, text: String) {
val newMessage = Gson().fromJson(text, Message::class.java)
runOnUiThread {
// 添加到消息列表并刷新UI
messageList.add(newMessage)
adapter.notifyItemInserted(messageList.size - 1)
recyclerView.scrollToPosition(messageList.size - 1)
}
// 存储到本地数据库
roomDatabase.messageDao().insert(newMessage)
}
})
}
// 发送消息
fun sendMessage(content: String) {
val message = Message(
id = UUID.randomUUID().toString(),
conversationId = "44_55", // 当前用户44与接收者55的会话ID
senderId = 44,
receiverId = 55,
content = content,
type = 1,
status = 0, // 发送中
timestamp = System.currentTimeMillis()
)
// 本地先展示,提升体验
messageList.add(message)
adapter.notifyItemInserted(messageList.size - 1)
// WebSocket发送到服务器
webSocket.send(Gson().toJson(message))
}
本地存储与历史加载(Room 数据库)
kotlin
// Room数据库定义
@Database(entities = [Message::class, Conversation::class], version = 1)
abstract class ChatDatabase : RoomDatabase() {
abstract fun messageDao(): MessageDao
abstract fun conversationDao(): ConversationDao
}
// 加载历史消息
fun loadHistoryMessages(conversationId: String) {
CoroutineScope(Dispatchers.IO).launch {
val history = chatDatabase.messageDao().getByConversationId(conversationId)
runOnUiThread {
messageList.addAll(history)
adapter.notifyDataSetChanged()
}
}
}
消息状态同步(已读回执)
kotlin
// 进入聊天页时,标记未读消息为已读
fun markAsRead(conversationId: String, receiverId: Int) {
CoroutineScope(Dispatchers.IO).launch {
// 1. 更新本地数据库
chatDatabase.messageDao().markAsRead(conversationId, receiverId)
// 2. 通知服务器更新状态
api.sendReadReceipt(conversationId, receiverId)
}
}
四、总结
核心问题集中在 “前端数据解析与 UI 赋值”,解决关键是确保前后端字段映射一致、正确处理嵌套数据结构;请求 Canceled 异常为良性优化,可通过统一请求管理优化;发消息功能需按 “数据模型→UI→实时通信→存储→状态同步” 的逻辑逐步实现,结合现有技术栈降低集成成本。所有问题解决后,用户信息页面可正常展示后端返回数据,发消息功能可实现类似微信的核心体验。
posted @
2025-11-04 23:54
棉花堂
阅读(
7)
评论()
收藏
举报