所遇到问题总结

  1. 前端 UI 数据展示异常(核心问题)
    后端接口 /api/auth/me 始终返回完整的用户数据(包括 phone="88888888888"、birthday="2005-08-08"、gender="1" 等字段),响应状态码均为 200,但 Android 端个人信息页面(PersonalInfoActivity)中,手机号、生日、性别等字段始终显示 “未设置”,仅用户名和角色能正常展示。从日志可见,前端解析时大部分字段(如 userId、realName、phone)被解析为空值,仅少数字段有效。
  2. 非致命性请求异常
    多次出现 “获取用户信息失败:Canceled” 日志,表现为重复发送 /api/auth/me 请求后,前一次请求被取消。但该异常未影响功能使用,因前端有本地缓存兜底,UI 未出现崩溃或卡顿。
  3. 功能扩展需求
    需要在现有 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)  评论(0)    收藏  举报